compositor: Support xdg_output_unstable_v1

The xdg-output resources are listed in each head struct. They become idle when
the respective weston_output has been removed again. The client is supposed to
destroy them explicitly afterwards.

After starting an XWayland client xrandr displays the logical size as expected.

Signed-off-by: Roman Gilg <subdiff@gmail.com>
diff --git a/libweston/compositor.c b/libweston/compositor.c
index a99deed..a7cd875 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -60,6 +60,7 @@
 #include "linux-dmabuf.h"
 #include "viewporter-server-protocol.h"
 #include "presentation-time-server-protocol.h"
+#include "xdg-output-unstable-v1-server-protocol.h"
 #include "linux-explicit-synchronization-unstable-v1-server-protocol.h"
 #include "linux-explicit-synchronization.h"
 #include "shared/fd-util.h"
@@ -123,6 +124,15 @@
 		if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
 			wl_output_send_done(resource);
 	}
+	wl_resource_for_each(resource, &head->xdg_output_resource_list) {
+		zxdg_output_v1_send_logical_position(resource,
+						     output->x,
+						     output->y);
+		zxdg_output_v1_send_logical_size(resource,
+						 output->width,
+						 output->height);
+		zxdg_output_v1_send_done(resource);
+	}
 }
 
 static void
@@ -4705,6 +4715,14 @@
 		wl_resource_set_destructor(resource, NULL);
 		wl_resource_set_user_data(resource, NULL);
 	}
+
+	wl_resource_for_each(resource, &head->xdg_output_resource_list) {
+		/* It's sufficient to unset the destructor, then the list elements
+		 * won't be accessed.
+		 */
+		wl_resource_set_destructor(resource, NULL);
+	}
+	wl_list_init(&head->xdg_output_resource_list);
 }
 
 /** Get the backing object of wl_output
@@ -4749,6 +4767,7 @@
 	wl_signal_init(&head->destroy_signal);
 	wl_list_init(&head->output_link);
 	wl_list_init(&head->resource_list);
+	wl_list_init(&head->xdg_output_resource_list);
 	head->name = strdup(name);
 }
 
@@ -5572,6 +5591,13 @@
 			if (ver >= WL_OUTPUT_DONE_SINCE_VERSION)
 				wl_output_send_done(resource);
 		}
+
+		wl_resource_for_each(resource, &head->xdg_output_resource_list) {
+			zxdg_output_v1_send_logical_position(resource,
+							     output->x,
+							     output->y);
+			zxdg_output_v1_send_done(resource);
+		}
 	}
 }
 
@@ -5790,6 +5816,15 @@
 			if (ver >= WL_OUTPUT_DONE_SINCE_VERSION)
 				wl_output_send_done(resource);
 		}
+		wl_resource_for_each(resource, &head->xdg_output_resource_list) {
+			zxdg_output_v1_send_logical_position(resource,
+							     output->x,
+							     output->y);
+			zxdg_output_v1_send_logical_size(resource,
+							 output->width,
+							 output->height);
+			zxdg_output_v1_send_done(resource);
+		}
 	}
 
 	/* we must ensure that pointers are inside output, otherwise they disappear */
@@ -6292,6 +6327,85 @@
 }
 
 static void
+xdg_output_unlist(struct wl_resource *resource)
+{
+	wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+xdg_output_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static const struct zxdg_output_v1_interface xdg_output_interface = {
+	xdg_output_destroy
+};
+
+static void
+xdg_output_manager_destroy(struct wl_client *client,
+                           struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void
+xdg_output_manager_get_xdg_output(struct wl_client *client,
+				  struct wl_resource *manager,
+				  uint32_t id,
+				  struct wl_resource *output_resource)
+{
+	int version = wl_resource_get_version(manager);
+	struct weston_head *head = wl_resource_get_user_data(output_resource);
+	struct weston_output *output = head->output;
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client, &zxdg_output_v1_interface,
+				      version, id);
+	if (resource == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_list_insert(&head->xdg_output_resource_list,
+		       wl_resource_get_link(resource));
+
+	wl_resource_set_implementation(resource, &xdg_output_interface,
+				       NULL, xdg_output_unlist);
+
+	zxdg_output_v1_send_logical_position(resource, output->x, output->y);
+	zxdg_output_v1_send_logical_size(resource,
+					 output->width,
+					 output->height);
+	if (version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)
+		zxdg_output_v1_send_name(resource, head->name);
+
+	zxdg_output_v1_send_done(resource);
+}
+
+static const struct zxdg_output_manager_v1_interface xdg_output_manager_interface = {
+	xdg_output_manager_destroy,
+	xdg_output_manager_get_xdg_output
+};
+
+static void
+bind_xdg_output_manager(struct wl_client *client,
+			void *data, uint32_t version, uint32_t id)
+{
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client, &zxdg_output_manager_v1_interface,
+				      version, id);
+	if (resource == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(resource, &xdg_output_manager_interface,
+				       NULL, NULL);
+}
+
+static void
 destroy_viewport(struct wl_resource *resource)
 {
 	struct weston_surface *surface =
@@ -6916,6 +7030,10 @@
 			      ec, bind_viewporter))
 		goto fail;
 
+	if (!wl_global_create(ec->wl_display, &zxdg_output_manager_v1_interface, 2,
+			      ec, bind_xdg_output_manager))
+		goto fail;
+
 	if (!wl_global_create(ec->wl_display, &wp_presentation_interface, 1,
 			      ec, bind_presentation))
 		goto fail;