xdg-shell: Replace the set_* atrocity with a new approach

Rather than require that the client implement two methods for every state,
simply have one global request, change_state, and one global event,
request_change_state.
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index 7811962..3b01462 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -161,8 +161,9 @@
 		bool maximized;
 		bool fullscreen;
 		bool relative;
-	} state, next_state; /* surface states */
+	} state, next_state, requested_state; /* surface states */
 	bool state_changed;
+	bool state_requested;
 
 	int focus_count;
 };
@@ -2288,8 +2289,6 @@
 	shsurf->fullscreen.type = method;
 	shsurf->fullscreen.framerate = framerate;
 
-	shsurf->next_state.fullscreen = true;
-	shsurf->state_changed = true;
 	shsurf->type = SHELL_SURFACE_TOPLEVEL;
 
 	shsurf->client->send_configure(shsurf->surface, 0,
@@ -2358,6 +2357,9 @@
 
 	surface_clear_next_states(shsurf);
 	set_fullscreen(shsurf, method, framerate, output);
+
+	shsurf->next_state.fullscreen = true;
+	shsurf->state_changed = true;
 }
 
 static void
@@ -2416,8 +2418,6 @@
 	                               shsurf->output->width,
 	                               shsurf->output->height - panel_height);
 
-	shsurf->next_state.maximized = true;
-	shsurf->state_changed = true;
 	shsurf->type = SHELL_SURFACE_TOPLEVEL;
 }
 
@@ -2459,6 +2459,9 @@
 
 	surface_clear_next_states(shsurf);
 	set_maximized(shsurf, output);
+
+	shsurf->next_state.maximized = true;
+	shsurf->state_changed = true;
 }
 
 /* This is only ever called from set_surface_type(), so there’s no need to
@@ -3243,87 +3246,95 @@
 }
 
 static void
-xdg_surface_set_fullscreen(struct wl_client *client,
-			   struct wl_resource *resource)
+xdg_surface_set_fullscreen(struct shell_surface *shsurf, int serial)
 {
-	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+	shsurf->requested_state.fullscreen = true;
+	shsurf->state_requested = true;
 
-	if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
-		return;
+	xdg_surface_send_change_state(shsurf->resource,
+				      XDG_SURFACE_STATE_FULLSCREEN, 1, serial);
 
-	if (!shsurf->next_state.fullscreen)
-		set_fullscreen(shsurf,
-			       WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
-			       0, shsurf->recommended_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)
+xdg_surface_unset_fullscreen(struct shell_surface *shsurf, int serial)
+{
+	shsurf->requested_state.fullscreen = false;
+	shsurf->state_requested = true;
+
+	xdg_surface_send_change_state(shsurf->resource,
+				      XDG_SURFACE_STATE_FULLSCREEN, 0, serial);
+}
+
+static void
+xdg_surface_set_maximized(struct shell_surface *shsurf, int serial)
+{
+	shsurf->requested_state.maximized = true;
+	shsurf->state_requested = true;
+
+	set_maximized(shsurf, NULL);
+
+	xdg_surface_send_change_state(shsurf->resource,
+				      XDG_SURFACE_STATE_MAXIMIZED, 1, serial);
+}
+static void
+xdg_surface_unset_maximized(struct shell_surface *shsurf, int serial)
+{
+	shsurf->requested_state.maximized = false;
+	shsurf->state_requested = true;
+
+	xdg_surface_send_change_state(shsurf->resource,
+				      XDG_SURFACE_STATE_MAXIMIZED, 0, serial);
+}
+
+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);
-	int32_t width, height;
+
+	/* 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;
 
-	if (!shsurf->next_state.fullscreen)
-		return;
-
-	shsurf->next_state.fullscreen = false;
-	shsurf->state_changed = true;
-
-	if (shsurf->saved_size_valid) {
-		width = shsurf->saved_width;
-		height = shsurf->saved_height;
-		shsurf->saved_size_valid = false;
-	} else {
-		width = shsurf->surface->width;
-		height = shsurf->surface->height;
+	switch (state) {
+	case XDG_SURFACE_STATE_MAXIMIZED:
+		if (value)
+			xdg_surface_set_maximized(shsurf, serial);
+		else
+			xdg_surface_unset_maximized(shsurf, serial);
+		break;
+	case XDG_SURFACE_STATE_FULLSCREEN:
+		if (value)
+			xdg_surface_set_fullscreen(shsurf, serial);
+		else
+			xdg_surface_unset_fullscreen(shsurf, serial);
+		break;
 	}
-
-	shsurf->client->send_configure(shsurf->surface, 0, width, height);
 }
 
 static void
-xdg_surface_set_maximized(struct wl_client *client,
-			  struct wl_resource *resource)
+xdg_surface_ack_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);
 
-	if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
-		return;
-
-	if (!shsurf->next_state.maximized)
-		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);
-	int32_t width, height;
-
-	if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
-		return;
-
-	if (!shsurf->next_state.maximized)
-		return;
-
-	shsurf->next_state.maximized = false;
-	shsurf->state_changed = true;
-
-	if (shsurf->saved_size_valid) {
-		width = shsurf->saved_width;
-		height = shsurf->saved_height;
-		shsurf->saved_size_valid = false;
-	} else {
-		width = shsurf->surface->width;
-		height = shsurf->surface->height;
+	if (shsurf->state_requested) {
+		shsurf->next_state = shsurf->requested_state;
+		shsurf->state_changed = true;
+		shsurf->state_requested = false;
 	}
-
-	shsurf->client->send_configure(shsurf->surface, 0, width, height);
 }
 
 static const struct xdg_surface_interface xdg_surface_implementation = {
@@ -3335,10 +3346,8 @@
 	xdg_surface_move,
 	xdg_surface_resize,
 	xdg_surface_set_output,
-	xdg_surface_set_fullscreen,
-	xdg_surface_unset_fullscreen,
-	xdg_surface_set_maximized,
-	xdg_surface_unset_maximized,
+	xdg_surface_request_change_state,
+	xdg_surface_ack_change_state,
 	NULL /* set_minimized */
 };
 
@@ -3925,9 +3934,9 @@
 		return;
 
 	if (shsurf->state.maximized)
-		xdg_surface_send_request_unset_maximized(shsurf->resource);
+		xdg_surface_unset_maximized(shsurf, wl_display_next_serial(seat->compositor->wl_display));
 	else
-		xdg_surface_send_request_set_maximized(shsurf->resource);
+		xdg_surface_set_maximized(shsurf, wl_display_next_serial(seat->compositor->wl_display));
 }
 
 static void
@@ -3949,9 +3958,9 @@
 		return;
 
 	if (shsurf->state.fullscreen)
-		xdg_surface_send_request_unset_fullscreen(shsurf->resource);
+		xdg_surface_unset_fullscreen(shsurf, wl_display_next_serial(seat->compositor->wl_display));
 	else
-		xdg_surface_send_request_set_fullscreen(shsurf->resource);
+		xdg_surface_set_fullscreen(shsurf, wl_display_next_serial(seat->compositor->wl_display));
 }
 
 static void