libweston: new head-based output management API
Introduce the API for users (compositors) to create an output from a
head, attach and detach heads, and destroy outputs created this way.
This also adds the backend-facing API to libweston.
In the new API design, a backend creates heads, and the compositor
chooses one or more heads (clone mode) to be driven by an output.
In the future backends will be converted to not create outputs directly
but only in the new create_output hook.
The user subscribes to a heads_changed hook and arranges heads into
outputs from there.
Adding the API this way will allow frontends (main.c) and backends to be
converted one by one. This adds compatiblity paths in
weston_compositor_create_output_with_head() and weston_output_destroy()
so that frontends can be converted first to call these, and then
backends can be converted one by one to the new design. Afterwards, the
compatibility paths will be removed along with weston_output::head.
Currently heads can be added to a disabled output only. This is less
than ideal for clone mode hotplug and should be improved on later.
v4: Remove the wl_output global on head detach if output is enabled.
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Derek Foreman <derekf@osg.samsung.com>
Reviewed-by: Ian Ray <ian.ray@ge.com>
Reviewed-by: Daniel Stone <daniels@collabora.com>
Acked-by: Derek Foreman <derekf@osg.samsung.com>
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 68bf11c..23e5ef0 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4434,7 +4434,7 @@
* \memberof weston_head
* \internal
*/
-static void
+WL_EXPORT void
weston_head_init(struct weston_head *head, const char *name)
{
/* Add some (in)sane defaults which can be used
@@ -4490,7 +4490,7 @@
* \memberof weston_compositor
* \internal
*/
-static void
+WL_EXPORT void
weston_compositor_add_head(struct weston_compositor *compositor,
struct weston_head *head)
{
@@ -4571,6 +4571,52 @@
return container_of(node, struct weston_head, compositor_link);
}
+/** Iterate over attached heads
+ *
+ * \param output The output whose heads to iterate.
+ * \param item The iterator, or NULL for start.
+ * \return The next attached head in the list.
+ *
+ * Returns all heads currently attached to the output.
+ *
+ * You can iterate over all heads as follows:
+ * \code
+ * struct weston_head *head = NULL;
+ *
+ * while ((head = weston_output_iterate_heads(output, head))) {
+ * ...
+ * }
+ * \endcode
+ *
+ * If you cause \c iter to be removed from the list, you cannot use it to
+ * continue iterating. Removing any other item is safe.
+ *
+ * \memberof weston_compositor
+ */
+WL_EXPORT struct weston_head *
+weston_output_iterate_heads(struct weston_output *output,
+ struct weston_head *iter)
+{
+ struct wl_list *list = &output->head_list;
+ struct wl_list *node;
+
+ assert(output);
+ assert(!iter || iter->output == output);
+
+ if (iter)
+ node = iter->output_link.next;
+ else
+ node = list->next;
+
+ assert(node);
+ assert(!iter || node != &iter->output_link);
+
+ if (node == list)
+ return NULL;
+
+ return container_of(node, struct weston_head, output_link);
+}
+
/** Attach a head to an inactive output
*
* \param output The output to attach to.
@@ -4590,7 +4636,7 @@
*
* \memberof weston_output
*/
-static int
+WL_EXPORT int
weston_output_attach_head(struct weston_output *output,
struct weston_head *head)
{
@@ -4600,9 +4646,13 @@
if (!wl_list_empty(&head->output_link))
return -1;
- /* XXX: no support for multi-head yet */
- if (!wl_list_empty(&output->head_list))
+ if (output->attach_head) {
+ if (output->attach_head(output, head) < 0)
+ return -1;
+ } else if (!wl_list_empty(&output->head_list)) {
+ /* No support for clones in the legacy path. */
return -1;
+ }
head->output = output;
wl_list_insert(output->head_list.prev, &head->output_link);
@@ -4616,14 +4666,33 @@
*
* It is safe to detach a non-attached head.
*
+ * If the head is attached to an enabled output and the output will be left
+ * with no heads, the output will be disabled.
+ *
* \memberof weston_head
+ * \sa weston_output_disable
*/
-static void
+WL_EXPORT void
weston_head_detach(struct weston_head *head)
{
+ struct weston_output *output = head->output;
+
wl_list_remove(&head->output_link);
wl_list_init(&head->output_link);
head->output = NULL;
+
+ if (!output)
+ return;
+
+ if (output->detach_head)
+ output->detach_head(output, head);
+
+ if (output->enabled) {
+ weston_head_remove_global(head);
+
+ if (wl_list_empty(&output->head_list))
+ weston_output_disable(output);
+ }
}
/** Destroy a head
@@ -4636,7 +4705,7 @@
* \memberof weston_head
* \internal
*/
-static void
+WL_EXPORT void
weston_head_release(struct weston_head *head)
{
weston_head_detach(head);
@@ -4806,6 +4875,31 @@
return head->output->enabled;
}
+/** Get the name of a head
+ *
+ * \param head The head to query.
+ * \return The head's name, not NULL.
+ *
+ * The name depends on the backend. The DRM backend uses connector names,
+ * other backends may use hardcoded names or user-given names.
+ */
+WL_EXPORT const char *
+weston_head_get_name(struct weston_head *head)
+{
+ return head->name;
+}
+
+/** Get the output the head is attached to
+ *
+ * \param head The head to query.
+ * \return The output the head is attached to, or NULL if detached.
+ */
+WL_EXPORT struct weston_output *
+weston_head_get_output(struct weston_head *head)
+{
+ return head->output;
+}
+
/* Move other outputs when one is resized so the space remains contiguous. */
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
@@ -5225,8 +5319,11 @@
wl_list_init(&output->head_list);
weston_head_init(&output->head, name);
- weston_head_set_connection_status(&output->head, true);
- weston_compositor_add_head(compositor, &output->head);
+ output->head.allocator_output = output;
+ if (!compositor->backend->create_output) {
+ weston_head_set_connection_status(&output->head, true);
+ weston_compositor_add_head(compositor, &output->head);
+ }
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
@@ -5465,6 +5562,78 @@
free(output->name);
}
+/** Create an output for an unused head
+ *
+ * \param compositor The compositor.
+ * \param head The head to attach to the output.
+ * \return A new \c weston_output, or NULL on failure.
+ *
+ * This creates a new weston_output that starts with the given head attached.
+ * The output inherits the name of the head. The head must not be already
+ * attached to another output.
+ *
+ * An output must be configured before it can be enabled.
+ *
+ * \memberof weston_compositor
+ */
+WL_EXPORT struct weston_output *
+weston_compositor_create_output_with_head(struct weston_compositor *compositor,
+ struct weston_head *head)
+{
+ struct weston_output *output;
+
+ if (head->allocator_output) {
+ /* XXX: compatibility path to be removed after all converted */
+ output = head->allocator_output;
+ } else {
+ assert(compositor->backend->create_output);
+ output = compositor->backend->create_output(compositor,
+ head->name);
+ }
+
+ if (!output)
+ return NULL;
+
+ if (weston_output_attach_head(output, head) < 0) {
+ if (!head->allocator_output)
+ output->destroy(output);
+
+ return NULL;
+ }
+
+ return output;
+}
+
+/** Destroy an output
+ *
+ * \param output The output to destroy.
+ *
+ * The heads attached to the given output are detached and become unused again.
+ *
+ * It is not necessary to explicitly destroy all outputs at compositor exit.
+ * weston_compositor_destroy() will automatically destroy any remaining
+ * outputs.
+ *
+ * \memberof weston_output
+ */
+WL_EXPORT void
+weston_output_destroy(struct weston_output *output)
+{
+ struct weston_head *head;
+
+ /* XXX: compatibility path to be removed after all converted */
+ head = weston_output_get_first_head(output);
+ if (head->allocator_output) {
+ /* The old design: backend is responsible for destroying the
+ * output, so just undo create_output_with_head()
+ */
+ weston_head_detach(head);
+ return;
+ }
+
+ output->destroy(output);
+}
+
/** When you need a head...
*
* This function is a hack, used until all code has been converted to become