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>
This commit is contained in:
Pekka Paalanen 2017-08-16 10:39:17 +03:00
parent 37e6c9e537
commit 992a8cb38c
2 changed files with 256 additions and 9 deletions

View File

@ -4434,7 +4434,7 @@ weston_head_from_resource(struct wl_resource *resource)
* \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 @@ weston_compositor_schedule_heads_changed(struct weston_compositor *compositor)
* \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 @@ weston_compositor_iterate_heads(struct weston_compositor *compositor,
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 @@ weston_compositor_iterate_heads(struct weston_compositor *compositor,
*
* \memberof weston_output
*/
static int
WL_EXPORT int
weston_output_attach_head(struct weston_output *output,
struct weston_head *head)
{
@ -4600,9 +4646,13 @@ weston_output_attach_head(struct weston_output *output,
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 @@ weston_output_attach_head(struct weston_output *output,
*
* 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 @@ weston_head_detach(struct weston_head *head)
* \memberof weston_head
* \internal
*/
static void
WL_EXPORT void
weston_head_release(struct weston_head *head)
{
weston_head_detach(head);
@ -4806,6 +4875,31 @@ weston_head_is_enabled(struct weston_head *head)
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 @@ weston_output_init(struct weston_output *output,
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 @@ weston_output_release(struct weston_output *output)
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

View File

@ -172,6 +172,8 @@ struct weston_head {
char *name; /**< head name, e.g. connector name */
bool connected; /**< is physically connected */
struct weston_output *allocator_output; /**< XXX: to be removed */
};
struct weston_output {
@ -263,6 +265,33 @@ struct weston_output {
int (*enable)(struct weston_output *output);
int (*disable)(struct weston_output *output);
/** Attach a head in the backend
*
* @param output The output to attach to.
* @param head The head to attach.
* @return 0 on success, -1 on failure.
*
* Do anything necessary to account for a new head being attached to
* the output, and check any conditions possible. On failure, both
* the head and the output must be left as before the call.
*
* Libweston core will add the head to the head_list after a successful
* call.
*/
int (*attach_head)(struct weston_output *output,
struct weston_head *head);
/** Detach a head in the backend
*
* @param output The output to detach from.
* @param head The head to detach.
*
* Do any clean-up necessary to detach this head from the output.
* The head has already been removed from the output's head_list.
*/
void (*detach_head)(struct weston_output *output,
struct weston_head *head);
};
enum weston_pointer_motion_mask {
@ -883,6 +912,21 @@ struct weston_backend {
*/
void (*repaint_flush)(struct weston_compositor *compositor,
void *repaint_data);
/** Allocate a new output
*
* @param compositor The compositor.
* @param name Name for the new output.
*
* Allocates a new output structure that embeds a weston_output,
* initializes it, and returns the pointer to the weston_output
* member.
*
* Must set weston_output members @c destroy, @c enable and @c disable.
*/
struct weston_output *
(*create_output)(struct weston_compositor *compositor,
const char *name);
};
struct weston_desktop_xwayland;
@ -1963,6 +2007,16 @@ weston_seat_set_keyboard_focus(struct weston_seat *seat,
int
weston_compositor_load_xwayland(struct weston_compositor *compositor);
void
weston_head_init(struct weston_head *head, const char *name);
void
weston_head_release(struct weston_head *head);
void
weston_compositor_add_head(struct weston_compositor *compositor,
struct weston_head *head);
void
weston_head_set_monitor_strings(struct weston_head *head,
const char *make,
@ -1989,6 +2043,15 @@ weston_head_is_connected(struct weston_head *head);
bool
weston_head_is_enabled(struct weston_head *head);
const char *
weston_head_get_name(struct weston_head *head);
struct weston_output *
weston_head_get_output(struct weston_head *head);
void
weston_head_detach(struct weston_head *head);
struct weston_head *
weston_compositor_iterate_heads(struct weston_compositor *compositor,
struct weston_head *iter);
@ -1997,6 +2060,21 @@ void
weston_compositor_add_heads_changed_listener(struct weston_compositor *compositor,
struct wl_listener *listener);
struct weston_output *
weston_compositor_create_output_with_head(struct weston_compositor *compositor,
struct weston_head *head);
void
weston_output_destroy(struct weston_output *output);
int
weston_output_attach_head(struct weston_output *output,
struct weston_head *head);
struct weston_head *
weston_output_iterate_heads(struct weston_output *output,
struct weston_head *iter);
void
weston_output_set_scale(struct weston_output *output,
int32_t scale);