xdg-shell: Rework the state system

The states system, so far, has been a complicated mix of weird APIs
that solved a real race condition, but have been particularly ugly
for both compositors and clients to implement.
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index ea0e049..574681f 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -3418,78 +3418,9 @@
 }
 
 static void
-xdg_surface_set_output(struct wl_client *client,
-		       struct wl_resource *resource,
-		       struct wl_resource *output_resource)
-{
-	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
-	struct weston_output *output;
-
-	if (output_resource)
-		output = wl_resource_get_user_data(output_resource);
-	else
-		output = NULL;
-
-	shsurf->recommended_output = output;
-}
-
-static void
-xdg_surface_change_state(struct shell_surface *shsurf,
-			 uint32_t state, uint32_t value, uint32_t serial)
-{
-	xdg_surface_send_change_state(shsurf->resource, state, value, serial);
-	shsurf->state_requested = true;
-
-	switch (state) {
-	case XDG_SURFACE_STATE_MAXIMIZED:
-		shsurf->requested_state.maximized = value;
-		if (value)
-			set_maximized(shsurf, NULL);
-		break;
-	case XDG_SURFACE_STATE_FULLSCREEN:
-		shsurf->requested_state.fullscreen = value;
-
-		if (value)
-			set_fullscreen(shsurf,
-				       WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
-				       0, shsurf->recommended_output);
-		break;
-	}
-}
-
-static void
-xdg_surface_request_change_state(struct wl_client *client,
-				 struct wl_resource *resource,
-				 uint32_t state,
-				 uint32_t value,
-				 uint32_t serial)
-{
-	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
-
-	/* The client can't know what the current state is, so we need
-	   to always send a state change in response. */
-
-	if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
-		return;
-
-	switch (state) {
-	case XDG_SURFACE_STATE_MAXIMIZED:
-	case XDG_SURFACE_STATE_FULLSCREEN:
-		break;
-	default:
-		/* send error? ignore? send change state with value 0? */
-		return;
-	}
-
-	xdg_surface_change_state(shsurf, state, value, serial);
-}
-
-static void
-xdg_surface_ack_change_state(struct wl_client *client,
-			     struct wl_resource *resource,
-			     uint32_t state,
-			     uint32_t value,
-			     uint32_t serial)
+xdg_surface_ack_configure(struct wl_client *client,
+			  struct wl_resource *resource,
+			  uint32_t serial)
 {
 	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
 
@@ -3501,6 +3432,62 @@
 }
 
 static void
+xdg_surface_set_maximized(struct wl_client *client,
+			  struct wl_resource *resource)
+{
+	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+	shsurf->state_requested = true;
+	shsurf->requested_state.maximized = true;
+	set_maximized(shsurf, NULL);
+}
+
+static void
+xdg_surface_unset_maximized(struct wl_client *client,
+			    struct wl_resource *resource)
+{
+	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+	shsurf->state_requested = true;
+	shsurf->requested_state.maximized = false;
+	shsurf->client->send_configure(shsurf->surface, 0, 0);
+}
+
+static void
+xdg_surface_set_fullscreen(struct wl_client *client,
+			   struct wl_resource *resource,
+			   struct wl_resource *output_resource)
+{
+	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+	struct weston_output *output;
+
+	shsurf->state_requested = true;
+	shsurf->requested_state.fullscreen = true;
+
+	if (output_resource)
+		output = wl_resource_get_user_data(output_resource);
+	else
+		output = NULL;
+
+	shsurf->recommended_output = output;
+
+	set_fullscreen(shsurf,
+		       WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+		       0, shsurf->recommended_output);
+}
+
+static void
+xdg_surface_unset_fullscreen(struct wl_client *client,
+			     struct wl_resource *resource)
+{
+	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+
+	shsurf->state_requested = true;
+	shsurf->requested_state.fullscreen = false;
+	shsurf->client->send_configure(shsurf->surface, 0, 0);
+}
+
+static void
 xdg_surface_set_minimized(struct wl_client *client,
 			    struct wl_resource *resource)
 {
@@ -3521,10 +3508,12 @@
 	xdg_surface_set_app_id,
 	xdg_surface_move,
 	xdg_surface_resize,
-	xdg_surface_set_output,
-	xdg_surface_request_change_state,
-	xdg_surface_ack_change_state,
-	xdg_surface_set_minimized
+	xdg_surface_ack_configure,
+	xdg_surface_set_maximized,
+	xdg_surface_unset_maximized,
+	xdg_surface_set_fullscreen,
+	xdg_surface_unset_fullscreen,
+	xdg_surface_set_minimized,
 };
 
 static void
@@ -3532,11 +3521,28 @@
 		   int32_t width, int32_t height)
 {
 	struct shell_surface *shsurf = get_shell_surface(surface);
+	uint32_t *s;
+	struct wl_array states;
+	uint32_t serial;
 
 	assert(shsurf);
 
-	if (shsurf->resource)
-		xdg_surface_send_configure(shsurf->resource, width, height);
+	if (!shsurf->resource)
+		return;
+
+	wl_array_init(&states);
+	if (shsurf->requested_state.fullscreen) {
+		s = wl_array_add(&states, sizeof *s);
+		*s = XDG_SURFACE_STATE_FULLSCREEN;
+	} else if (shsurf->requested_state.maximized) {
+		s = wl_array_add(&states, sizeof *s);
+		*s = XDG_SURFACE_STATE_MAXIMIZED;
+	}
+
+	serial = wl_display_next_serial(shsurf->surface->compositor->wl_display);
+	xdg_surface_send_configure(shsurf->resource, width, height, &states, serial);
+
+	wl_array_release(&states);
 }
 
 static const struct weston_shell_client xdg_client = {
@@ -4107,7 +4113,6 @@
 	struct weston_surface *focus = seat->keyboard->focus;
 	struct weston_surface *surface;
 	struct shell_surface *shsurf;
-	uint32_t serial;
 
 	surface = weston_surface_get_main_surface(focus);
 	if (surface == NULL)
@@ -4120,9 +4125,10 @@
 	if (!shell_surface_is_xdg_surface(shsurf))
 		return;
 
-	serial = wl_display_next_serial(seat->compositor->wl_display);
-	xdg_surface_change_state(shsurf, XDG_SURFACE_STATE_MAXIMIZED,
-				 !shsurf->state.maximized, serial);
+	shsurf->state_requested = true;
+	shsurf->requested_state.maximized = !shsurf->state.maximized;
+	if (shsurf->requested_state.maximized)
+		set_maximized(shsurf, NULL);
 }
 
 static void
@@ -4131,7 +4137,6 @@
 	struct weston_surface *focus = seat->keyboard->focus;
 	struct weston_surface *surface;
 	struct shell_surface *shsurf;
-	uint32_t serial;
 
 	surface = weston_surface_get_main_surface(focus);
 	if (surface == NULL)
@@ -4144,9 +4149,12 @@
 	if (!shell_surface_is_xdg_surface(shsurf))
 		return;
 
-	serial = wl_display_next_serial(seat->compositor->wl_display);
-	xdg_surface_change_state(shsurf, XDG_SURFACE_STATE_FULLSCREEN,
-				 !shsurf->state.fullscreen, serial);
+	shsurf->state_requested = true;
+	shsurf->requested_state.fullscreen = !shsurf->state.fullscreen;
+	if (shsurf->requested_state.fullscreen)
+		set_fullscreen(shsurf,
+			       WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+			       0, shsurf->recommended_output);
 }
 
 static void