Discussion:
[PATCH weston v5 00/36] Head-based output configuration API a.k.a clone mode infrastructure
(too old to reply)
Pekka Paalanen
2017-12-14 11:40:37 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Hi all,

this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.

Previous submission with rationale:
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html

Design document:
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/

The goal:
https://phabricator.freedesktop.org/T7727

Changes in v5 compared to v3 are minor:
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.

You can find this series in the branch:
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5

I went through the same testing procedure as with v3, the previous submission.


However, the interesting bits are in the branch:
https://gitlab.collabora.com/pq/weston/commits/clonemode-4

As new things compared to clonemode-user-API-2, that one contains:
- support for configuring clone mode in weston.ini
- main.c implementation to configure a clode using the new API
- desktop-shell enhancements to avoid redundant panel and background surfaces
in clone mode
- hooking up custom data to weston_output via a new user-side destroy signal
- naming outputs freely
- DRM-backend support for shared-CRTC clone mode
- video mode list merging in the DRM-backend

The shared-CRTC clone mode has been tested to work on an i.MX6 device.
Unfortunately it seems that PC hardware that would support it is becoming
scarce AFAIU.

Most of the patches in clonemode-4 depend on the atomic modesetting series and
can therefore be submitted only after that one.


Thanks,
pq

Pekka Paalanen (36):
libweston: introduce weston_head
libweston: move wl_output to weston_head
libweston: use head in wl_output global
libweston: make wl_output point to weston_head
libweston: refactor weston_mode_switch_finish
libweston: introduce weston_output::head_list
libweston: properly orphan wl_output resources
libweston: strdup head make, model, serial_number
cms-colord: find a good head
libweston: add name to weston_head
libweston: add weston_head::connected
libweston: add compositor list of heads
libweston: add heads_changed hook
libweston: new head-based output management API
libweston: add weston_head destroy signal
libweston: add weston_head_is_device_changed() API
weston: move weston_output_enable() into callers
weston: migrate headless to head-based output API
weston: migrate x11 to head-based output API
weston: migrate wayland to head-based output API
weston: migrate fbdev to head-based output API
weston: migrate RDP to head-based output API
weston: migrate DRM to head-based output API
libweston: change windowed_output_api output_create to create_head
libweston: remove output_pending_signal
libweston: stop auto-adding the implicit head
libweston: assert make/model in weston_output_enable()
libweston: assert current_mode in weston_output_enable()
libweston: cancel idle_repaint on output destroy
compositor-headless: migrate to head-based output API
compositor-rdp: migrate to head-based output API
compositor-fbdev: make re-enable less drastic
compositor-fbdev: migrate to head-based output API
compositor-x11: migrate to head-based output API
compositor-wayland: strict surface create/destroy
compositor-wayland: migrate to head-based output API

compositor/cms-colord.c | 38 +-
compositor/main.c | 304 +++++++++---
compositor/weston-screenshooter.c | 2 +-
desktop-shell/input-panel.c | 4 +-
desktop-shell/shell.c | 4 +-
fullscreen-shell/fullscreen-shell.c | 4 +-
ivi-shell/input-panel-ivi.c | 4 +-
libweston-desktop/wl-shell.c | 2 +-
libweston-desktop/xdg-shell-v5.c | 2 +-
libweston-desktop/xdg-shell-v6.c | 2 +-
libweston/compositor-drm.c | 15 +-
libweston/compositor-fbdev.c | 206 +++++---
libweston/compositor-headless.c | 75 ++-
libweston/compositor-rdp.c | 64 ++-
libweston/compositor-wayland.c | 254 +++++++---
libweston/compositor-x11.c | 71 ++-
libweston/compositor.c | 948 +++++++++++++++++++++++++++++++++---
libweston/compositor.h | 182 ++++++-
libweston/windowed-output-api.h | 23 +-
tests/weston-test.c | 2 +-
20 files changed, 1832 insertions(+), 374 deletions(-)
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:38 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

In order to support clone modes, libweston needs the concept of a head
that is separate from weston_output. While weston_output manages buffers
and the repaint state machine, weston_head will represent a single
monitor. In the future it will be possible to have a single
weston_output drive one or more weston_heads for a clone mode that
shares the framebuffers between all cloned heads.

All the fields that are obviously properties of the monitor are moved
from weston_output into weston_head.

As moving the fields requires one to touch all the backends for all the
assingments, introduce setter functions for them while we are here. The
setters are identical to the old assignments, for now.

As a temporary measure, weston_output embeds a single head. Also the
ugly casts in weston_head_set_monitor_strings() will be removed by a
follow-up patch.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/cms-colord.c | 32 ++++++------
libweston/compositor-drm.c | 15 +++---
libweston/compositor-fbdev.c | 12 +++--
libweston/compositor-headless.c | 8 +--
libweston/compositor-rdp.c | 8 +--
libweston/compositor-wayland.c | 18 ++++---
libweston/compositor-x11.c | 13 +++--
libweston/compositor.c | 105 +++++++++++++++++++++++++++++++++++-----
libweston/compositor.h | 38 +++++++++++++--
9 files changed, 184 insertions(+), 65 deletions(-)

diff --git a/compositor/cms-colord.c b/compositor/cms-colord.c
index 0daa2a7e..f421773b 100644
--- a/compositor/cms-colord.c
+++ b/compositor/cms-colord.c
@@ -102,22 +102,23 @@ edid_value_valid(const char *str)
static gchar *
get_output_id(struct cms_colord *cms, struct weston_output *o)
{
+ struct weston_head *head = &o->head;
const gchar *tmp;
GString *device_id;

/* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
* for format and allowed values */
device_id = g_string_new("xrandr");
- if (edid_value_valid(o->make)) {
- tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
+ if (edid_value_valid(head->make)) {
+ tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
if (tmp == NULL)
- tmp = o->make;
+ tmp = head->make;
g_string_append_printf(device_id, "-%s", tmp);
}
- if (edid_value_valid(o->model))
- g_string_append_printf(device_id, "-%s", o->model);
- if (edid_value_valid(o->serial_number))
- g_string_append_printf(device_id, "-%s", o->serial_number);
+ if (edid_value_valid(head->model))
+ g_string_append_printf(device_id, "-%s", head->model);
+ if (edid_value_valid(head->serial_number))
+ g_string_append_printf(device_id, "-%s", head->serial_number);

/* no EDID data, so use fallback */
if (strcmp(device_id->str, "xrandr") == 0)
@@ -230,6 +231,7 @@ colord_notifier_output_destroy(struct wl_listener *listener, void *data)
static void
colord_output_created(struct cms_colord *cms, struct weston_output *o)
{
+ struct weston_head *head = &o->head;
CdDevice *device;
const gchar *tmp;
gchar *device_id;
@@ -251,25 +253,25 @@ colord_output_created(struct cms_colord *cms, struct weston_output *o)
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_COLORSPACE),
g_strdup(cd_colorspace_to_string(CD_COLORSPACE_RGB)));
- if (edid_value_valid(o->make)) {
- tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
+ if (edid_value_valid(head->make)) {
+ tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
if (tmp == NULL)
- tmp = o->make;
+ tmp = head->make;
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_VENDOR),
g_strdup(tmp));
}
- if (edid_value_valid(o->model)) {
+ if (edid_value_valid(head->model)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_MODEL),
- g_strdup(o->model));
+ g_strdup(head->model));
}
- if (edid_value_valid(o->serial_number)) {
+ if (edid_value_valid(head->serial_number)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_SERIAL),
- g_strdup(o->serial_number));
+ g_strdup(head->serial_number));
}
- if (o->connection_internal) {
+ if (head->connection_internal) {
g_hash_table_insert (device_props,
g_strdup (CD_DEVICE_PROPERTY_EMBEDDED),
NULL);
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b77209c4..b312f5d4 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3339,6 +3339,7 @@ create_output_for_connector(struct drm_backend *b,
struct udev_device *drm_device)
{
struct drm_output *output;
+ struct weston_head *head;
drmModeObjectPropertiesPtr props;
struct drm_mode *drm_mode;
char *name;
@@ -3391,19 +3392,19 @@ create_output_for_connector(struct drm_backend *b,
}
drm_property_info_populate(b, connector_props, output->props_conn,
WDRM_CONNECTOR__COUNT, props);
+ head = &output->base.head;
find_and_parse_output_edid(b, output, props,
&make, &model, &serial_number);
- output->base.make = (char *)make;
- output->base.model = (char *)model;
- output->base.serial_number = (char *)serial_number;
- output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel);
+ weston_head_set_monitor_strings(head, make, model, serial_number);
+ weston_head_set_subpixel(head,
+ drm_subpixel_to_wayland(output->connector->subpixel));

if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- output->base.connection_internal = true;
+ weston_head_set_internal(head);

- output->base.mm_width = output->connector->mmWidth;
- output->base.mm_height = output->connector->mmHeight;
+ weston_head_set_physical_size(head, output->connector->mmWidth,
+ output->connector->mmHeight);

drmModeFreeObjectProperties(props);

diff --git a/libweston/compositor-fbdev.c b/libweston/compositor-fbdev.c
index fbab634b..c71d7eb5 100644
--- a/libweston/compositor-fbdev.c
+++ b/libweston/compositor-fbdev.c
@@ -499,6 +499,7 @@ fbdev_output_create(struct fbdev_backend *backend,
const char *device)
{
struct fbdev_output *output;
+ struct weston_head *head;
int fb_fd;

weston_log("Creating fbdev output.\n");
@@ -532,12 +533,13 @@ fbdev_output_create(struct fbdev_backend *backend,
wl_list_insert(&output->base.mode_list, &output->mode.link);

output->base.current_mode = &output->mode;
- output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
- output->base.make = "unknown";
- output->base.model = output->fb_info.id;

- output->base.mm_width = output->fb_info.width_mm;
- output->base.mm_height = output->fb_info.height_mm;
+ head = &output->base.head;
+ weston_head_set_monitor_strings(head, "unknown", output->fb_info.id,
+ NULL);
+ weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_UNKNOWN);
+ weston_head_set_physical_size(head, output->fb_info.width_mm,
+ output->fb_info.height_mm);

close(fb_fd);

diff --git a/libweston/compositor-headless.c b/libweston/compositor-headless.c
index 2f01b64a..8fbb83ff 100644
--- a/libweston/compositor-headless.c
+++ b/libweston/compositor-headless.c
@@ -185,6 +185,7 @@ headless_output_set_size(struct weston_output *base,
int width, int height)
{
struct headless_output *output = to_headless_output(base);
+ struct weston_head *head = &output->base.head;
int output_width, output_height;

/* We can only be called once. */
@@ -204,12 +205,11 @@ headless_output_set_size(struct weston_output *base,
wl_list_insert(&output->base.mode_list, &output->mode.link);

output->base.current_mode = &output->mode;
- output->base.make = "weston";
- output->base.model = "headless";
+
+ weston_head_set_monitor_strings(head, "weston", "headless", NULL);

/* XXX: Calculate proper size. */
- output->base.mm_width = width;
- output->base.mm_height = height;
+ weston_head_set_physical_size(head, width, height);

output->base.start_repaint_loop = headless_output_start_repaint_loop;
output->base.repaint = headless_output_repaint;
diff --git a/libweston/compositor-rdp.c b/libweston/compositor-rdp.c
index 98286737..ffad52a6 100644
--- a/libweston/compositor-rdp.c
+++ b/libweston/compositor-rdp.c
@@ -482,6 +482,7 @@ rdp_output_set_size(struct weston_output *base,
int width, int height)
{
struct rdp_output *output = to_rdp_output(base);
+ struct weston_head *head = &output->base.head;
struct weston_mode *currentMode;
struct weston_mode initMode;

@@ -500,12 +501,11 @@ rdp_output_set_size(struct weston_output *base,
return -1;

output->base.current_mode = output->base.native_mode = currentMode;
- output->base.make = "weston";
- output->base.model = "rdp";
+
+ weston_head_set_monitor_strings(head, "weston", "rdp", NULL);

/* XXX: Calculate proper size. */
- output->base.mm_width = width;
- output->base.mm_height = height;
+ weston_head_set_physical_size(head, width, height);

output->base.start_repaint_loop = rdp_output_start_repaint_loop;
output->base.repaint = rdp_output_repaint;
diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c
index 3bdfb03e..040c40b9 100644
--- a/libweston/compositor-wayland.c
+++ b/libweston/compositor-wayland.c
@@ -1313,6 +1313,7 @@ static int
wayland_output_set_size(struct weston_output *base, int width, int height)
{
struct wayland_output *output = to_wayland_output(base);
+ struct weston_head *head = &output->base.head;
int output_width, output_height;

/* We can only be called once. */
@@ -1345,12 +1346,11 @@ wayland_output_set_size(struct weston_output *base, int width, int height)
wl_list_insert(&output->base.mode_list, &output->mode.link);

output->base.current_mode = &output->mode;
- output->base.make = "wayland";
- output->base.model = "none";
+
+ weston_head_set_monitor_strings(head, "wayland", "none", NULL);

/* XXX: Calculate proper size. */
- output->base.mm_width = width;
- output->base.mm_height = height;
+ weston_head_set_physical_size(head, width, height);

return 0;
}
@@ -1383,10 +1383,12 @@ wayland_output_create_for_parent_output(struct wayland_backend *b,

output->parent.output = poutput->global;

- output->base.make = poutput->physical.make;
- output->base.model = poutput->physical.model;
- output->base.mm_width = poutput->physical.width;
- output->base.mm_height = poutput->physical.height;
+ weston_head_set_monitor_strings(&output->base.head,
+ poutput->physical.make,
+ poutput->physical.model, NULL);
+ weston_head_set_physical_size(&output->base.head,
+ poutput->physical.width,
+ poutput->physical.height);

wl_list_insert_list(&output->base.mode_list, &poutput->mode_list);
wl_list_init(&poutput->mode_list);
diff --git a/libweston/compositor-x11.c b/libweston/compositor-x11.c
index fd948540..b800132a 100644
--- a/libweston/compositor-x11.c
+++ b/libweston/compositor-x11.c
@@ -1061,6 +1061,8 @@ x11_output_set_size(struct weston_output *base, int width, int height)
{
struct x11_output *output = to_x11_output(base);
struct x11_backend *b = to_x11_backend(base->compositor);
+ struct weston_head *head = &output->base.head;
+ xcb_screen_t *scrn = b->screen;
int output_width, output_height;

/* We can only be called once. */
@@ -1095,16 +1097,13 @@ x11_output_set_size(struct weston_output *base, int width, int height)
wl_list_insert(&output->base.mode_list, &output->mode.link);

output->base.current_mode = &output->mode;
- output->base.make = "weston-X11";
- output->base.model = "none";
-
output->base.native_mode = &output->native;
output->base.native_scale = output->base.scale;

- output->base.mm_width = width * b->screen->width_in_millimeters /
- b->screen->width_in_pixels;
- output->base.mm_height = height * b->screen->height_in_millimeters /
- b->screen->height_in_pixels;
+ weston_head_set_monitor_strings(head, "weston-X11", "none", NULL);
+ weston_head_set_physical_size(head,
+ width * scrn->width_in_millimeters / scrn->width_in_pixels,
+ height * scrn->height_in_millimeters / scrn->height_in_pixels);

return 0;
}
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 7d7a17ed..04ac65d2 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4313,6 +4313,7 @@ bind_output(struct wl_client *client,
struct weston_output *output = data;
struct weston_mode *mode;
struct wl_resource *resource;
+ struct weston_head *head = &output->head;

resource = wl_resource_create(client, &wl_output_interface,
version, id);
@@ -4327,10 +4328,10 @@ bind_output(struct wl_client *client,
wl_output_send_geometry(resource,
output->x,
output->y,
- output->mm_width,
- output->mm_height,
- output->subpixel,
- output->make, output->model,
+ head->mm_width,
+ head->mm_height,
+ head->subpixel,
+ head->make, head->model,
output->transform);
if (version >= WL_OUTPUT_SCALE_SINCE_VERSION)
wl_output_send_scale(resource,
@@ -4363,6 +4364,85 @@ weston_output_from_resource(struct wl_resource *resource)
return wl_resource_get_user_data(resource);
}

+/** Store monitor make, model and serial number
+ *
+ * \param head The head to modify.
+ * \param make The monitor make. If EDID is available, the PNP ID. Otherwise
+ * any string, or NULL for none.
+ * \param model The monitor model or name, or a made-up string, or NULL for
+ * none.
+ * \param serialno The monitor serial number, a made-up string, or NULL for
+ * none.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_monitor_strings(struct weston_head *head,
+ const char *make,
+ const char *model,
+ const char *serialno)
+{
+ head->make = (char *)make;
+ head->model = (char *)model;
+ head->serial_number = (char *)serialno;
+}
+
+/** Store physical image size
+ *
+ * \param head The head to modify.
+ * \param mm_width Image area width in millimeters.
+ * \param mm_height Image area height in millimeters.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_physical_size(struct weston_head *head,
+ int32_t mm_width, int32_t mm_height)
+{
+ head->mm_width = mm_width;
+ head->mm_height = mm_height;
+}
+
+/** Store monitor sub-pixel layout
+ *
+ * \param head The head to modify.
+ * \param sp Sub-pixel layout. The possible values are:
+ * - WL_OUTPUT_SUBPIXEL_UNKNOWN,
+ * - WL_OUTPUT_SUBPIXEL_NONE,
+ * - WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB,
+ * - WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR,
+ * - WL_OUTPUT_SUBPIXEL_VERTICAL_RGB,
+ * - WL_OUTPUT_SUBPIXEL_VERTICAL_BGR
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_subpixel(struct weston_head *head,
+ enum wl_output_subpixel sp)
+{
+ head->subpixel = sp;
+}
+
+/** Mark the monitor as internal
+ *
+ * This is used for embedded screens, like laptop panels.
+ *
+ * \param head The head to mark as internal.
+ *
+ * By default a head is external. The type is often inferred from the physical
+ * connector type.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_internal(struct weston_head *head)
+{
+ head->connection_internal = true;
+}

/* Move other outputs when one is resized so the space remains contiguous. */
static void
@@ -4479,6 +4559,7 @@ weston_output_init_geometry(struct weston_output *output, int x, int y)
WL_EXPORT void
weston_output_move(struct weston_output *output, int x, int y)
{
+ struct weston_head *head = &output->head;
struct wl_resource *resource;

output->move_x = x - output->x;
@@ -4499,11 +4580,11 @@ weston_output_move(struct weston_output *output, int x, int y)
wl_output_send_geometry(resource,
output->x,
output->y,
- output->mm_width,
- output->mm_height,
- output->subpixel,
- output->make,
- output->model,
+ head->mm_width,
+ head->mm_height,
+ head->subpixel,
+ head->make,
+ head->model,
output->transform);

if (wl_resource_get_version(resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
@@ -4721,6 +4802,8 @@ weston_output_init(struct weston_output *output,
struct weston_compositor *compositor,
const char *name)
{
+ struct weston_head *head = &output->head;
+
output->compositor = compositor;
output->destroying = 0;
output->name = strdup(name);
@@ -4730,8 +4813,8 @@ weston_output_init(struct weston_output *output,
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
*/
- output->mm_width = 0;
- output->mm_height = 0;
+ head->mm_width = 0;
+ head->mm_height = 0;
output->scale = 0;
/* Can't use -1 on uint32_t and 0 is valid enum value */
output->transform = UINT32_MAX;
diff --git a/libweston/compositor.h b/libweston/compositor.h
index dffcba89..b1bed44c 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -147,6 +147,21 @@ enum dpms_enum {
WESTON_DPMS_OFF
};

+/** Represents a monitor
+ *
+ * This object represents a monitor (hardware backends like DRM) or a window
+ * (windowed nested backends).
+ */
+struct weston_head {
+ int32_t mm_width; /**< physical image width in mm */
+ int32_t mm_height; /**< physical image height in mm */
+ char *make; /**< monitor manufacturer (PNP ID) */
+ char *model; /**< monitor model */
+ char *serial_number; /**< monitor serial */
+ uint32_t subpixel; /**< enum wl_output_subpixel */
+ bool connection_internal; /**< embedded monitor (e.g. laptop) */
+};
+
struct weston_output {
uint32_t id;
char *name;
@@ -165,7 +180,6 @@ struct weston_output {

struct wl_list animation_list;
int32_t x, y, width, height;
- int32_t mm_width, mm_height;

/** Output area in global coordinates, simple rect */
pixman_region32_t region;
@@ -199,8 +213,6 @@ struct weston_output {
int destroying;
struct wl_list feedback_list;

- char *make, *model, *serial_number;
- uint32_t subpixel;
uint32_t transform;
int32_t native_scale;
int32_t current_scale;
@@ -211,6 +223,8 @@ struct weston_output {
struct weston_mode *original_mode;
struct wl_list mode_list;

+ struct weston_head head;
+
void (*start_repaint_loop)(struct weston_output *output);
int (*repaint)(struct weston_output *output,
pixman_region32_t *damage,
@@ -224,7 +238,6 @@ struct weston_output {
void (*set_backlight)(struct weston_output *output, uint32_t value);
void (*set_dpms)(struct weston_output *output, enum dpms_enum level);

- bool connection_internal;
uint16_t gamma_size;
void (*set_gamma)(struct weston_output *output,
uint16_t size,
@@ -1932,6 +1945,23 @@ int
weston_compositor_load_xwayland(struct weston_compositor *compositor);

void
+weston_head_set_monitor_strings(struct weston_head *head,
+ const char *make,
+ const char *model,
+ const char *serialno);
+
+void
+weston_head_set_physical_size(struct weston_head *head,
+ int32_t mm_width, int32_t mm_height);
+
+void
+weston_head_set_subpixel(struct weston_head *head,
+ enum wl_output_subpixel sp);
+
+void
+weston_head_set_internal(struct weston_head *head);
+
+void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
--
2.13.6
Derek Foreman
2018-02-01 19:59:24 UTC
Permalink
Post by Pekka Paalanen
In order to support clone modes, libweston needs the concept of a head
that is separate from weston_output. While weston_output manages buffers
and the repaint state machine, weston_head will represent a single
monitor. In the future it will be possible to have a single
weston_output drive one or more weston_heads for a clone mode that
shares the framebuffers between all cloned heads.
All the fields that are obviously properties of the monitor are moved
from weston_output into weston_head.
As moving the fields requires one to touch all the backends for all the
assingments, introduce setter functions for them while we are here. The
setters are identical to the old assignments, for now.
As a temporary measure, weston_output embeds a single head. Also the
ugly casts in weston_head_set_monitor_strings() will be removed by a
follow-up patch.
---
compositor/cms-colord.c | 32 ++++++------
libweston/compositor-drm.c | 15 +++---
libweston/compositor-fbdev.c | 12 +++--
libweston/compositor-headless.c | 8 +--
libweston/compositor-rdp.c | 8 +--
libweston/compositor-wayland.c | 18 ++++---
libweston/compositor-x11.c | 13 +++--
libweston/compositor.c | 105 +++++++++++++++++++++++++++++++++++-----
libweston/compositor.h | 38 +++++++++++++--
9 files changed, 184 insertions(+), 65 deletions(-)
diff --git a/compositor/cms-colord.c b/compositor/cms-colord.c
index 0daa2a7e..f421773b 100644
--- a/compositor/cms-colord.c
+++ b/compositor/cms-colord.c
@@ -102,22 +102,23 @@ edid_value_valid(const char *str)
static gchar *
get_output_id(struct cms_colord *cms, struct weston_output *o)
{
+ struct weston_head *head = &o->head;
const gchar *tmp;
GString *device_id;
/* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
* for format and allowed values */
device_id = g_string_new("xrandr");
- if (edid_value_valid(o->make)) {
- tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
+ if (edid_value_valid(head->make)) {
+ tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
if (tmp == NULL)
- tmp = o->make;
+ tmp = head->make;
g_string_append_printf(device_id, "-%s", tmp);
}
- if (edid_value_valid(o->model))
- g_string_append_printf(device_id, "-%s", o->model);
- if (edid_value_valid(o->serial_number))
- g_string_append_printf(device_id, "-%s", o->serial_number);
+ if (edid_value_valid(head->model))
+ g_string_append_printf(device_id, "-%s", head->model);
+ if (edid_value_valid(head->serial_number))
+ g_string_append_printf(device_id, "-%s", head->serial_number);
/* no EDID data, so use fallback */
if (strcmp(device_id->str, "xrandr") == 0)
@@ -230,6 +231,7 @@ colord_notifier_output_destroy(struct wl_listener *listener, void *data)
static void
colord_output_created(struct cms_colord *cms, struct weston_output *o)
{
+ struct weston_head *head = &o->head;
CdDevice *device;
const gchar *tmp;
gchar *device_id;
@@ -251,25 +253,25 @@ colord_output_created(struct cms_colord *cms, struct weston_output *o)
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_COLORSPACE),
g_strdup(cd_colorspace_to_string(CD_COLORSPACE_RGB)));
- if (edid_value_valid(o->make)) {
- tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
+ if (edid_value_valid(head->make)) {
+ tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
if (tmp == NULL)
- tmp = o->make;
+ tmp = head->make;
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_VENDOR),
g_strdup(tmp));
}
- if (edid_value_valid(o->model)) {
+ if (edid_value_valid(head->model)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_MODEL),
- g_strdup(o->model));
+ g_strdup(head->model));
}
- if (edid_value_valid(o->serial_number)) {
+ if (edid_value_valid(head->serial_number)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_SERIAL),
- g_strdup(o->serial_number));
+ g_strdup(head->serial_number));
}
- if (o->connection_internal) {
+ if (head->connection_internal) {
g_hash_table_insert (device_props,
g_strdup (CD_DEVICE_PROPERTY_EMBEDDED),
NULL);
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b77209c4..b312f5d4 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3339,6 +3339,7 @@ create_output_for_connector(struct drm_backend *b,
struct udev_device *drm_device)
{
struct drm_output *output;
+ struct weston_head *head;
drmModeObjectPropertiesPtr props;
struct drm_mode *drm_mode;
char *name;
@@ -3391,19 +3392,19 @@ create_output_for_connector(struct drm_backend *b,
}
drm_property_info_populate(b, connector_props, output->props_conn,
WDRM_CONNECTOR__COUNT, props);
+ head = &output->base.head;
find_and_parse_output_edid(b, output, props,
&make, &model, &serial_number);
- output->base.make = (char *)make;
- output->base.model = (char *)model;
- output->base.serial_number = (char *)serial_number;
- output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel);
+ weston_head_set_monitor_strings(head, make, model, serial_number);
+ weston_head_set_subpixel(head,
+ drm_subpixel_to_wayland(output->connector->subpixel));
if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- output->base.connection_internal = true;
+ weston_head_set_internal(head);
- output->base.mm_width = output->connector->mmWidth;
- output->base.mm_height = output->connector->mmHeight;
+ weston_head_set_physical_size(head, output->connector->mmWidth,
+ output->connector->mmHeight);
drmModeFreeObjectProperties(props);
diff --git a/libweston/compositor-fbdev.c b/libweston/compositor-fbdev.c
index fbab634b..c71d7eb5 100644
--- a/libweston/compositor-fbdev.c
+++ b/libweston/compositor-fbdev.c
@@ -499,6 +499,7 @@ fbdev_output_create(struct fbdev_backend *backend,
const char *device)
{
struct fbdev_output *output;
+ struct weston_head *head;
int fb_fd;
weston_log("Creating fbdev output.\n");
@@ -532,12 +533,13 @@ fbdev_output_create(struct fbdev_backend *backend,
wl_list_insert(&output->base.mode_list, &output->mode.link);
output->base.current_mode = &output->mode;
- output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
- output->base.make = "unknown";
- output->base.model = output->fb_info.id;
- output->base.mm_width = output->fb_info.width_mm;
- output->base.mm_height = output->fb_info.height_mm;
+ head = &output->base.head;
+ weston_head_set_monitor_strings(head, "unknown", output->fb_info.id,
+ NULL);
+ weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_UNKNOWN);
+ weston_head_set_physical_size(head, output->fb_info.width_mm,
+ output->fb_info.height_mm);
close(fb_fd);
diff --git a/libweston/compositor-headless.c b/libweston/compositor-headless.c
index 2f01b64a..8fbb83ff 100644
--- a/libweston/compositor-headless.c
+++ b/libweston/compositor-headless.c
@@ -185,6 +185,7 @@ headless_output_set_size(struct weston_output *base,
int width, int height)
{
struct headless_output *output = to_headless_output(base);
+ struct weston_head *head = &output->base.head;
int output_width, output_height;
/* We can only be called once. */
@@ -204,12 +205,11 @@ headless_output_set_size(struct weston_output *base,
wl_list_insert(&output->base.mode_list, &output->mode.link);
output->base.current_mode = &output->mode;
- output->base.make = "weston";
- output->base.model = "headless";
+
+ weston_head_set_monitor_strings(head, "weston", "headless", NULL);
/* XXX: Calculate proper size. */
- output->base.mm_width = width;
- output->base.mm_height = height;
+ weston_head_set_physical_size(head, width, height);
output->base.start_repaint_loop = headless_output_start_repaint_loop;
output->base.repaint = headless_output_repaint;
diff --git a/libweston/compositor-rdp.c b/libweston/compositor-rdp.c
index 98286737..ffad52a6 100644
--- a/libweston/compositor-rdp.c
+++ b/libweston/compositor-rdp.c
@@ -482,6 +482,7 @@ rdp_output_set_size(struct weston_output *base,
int width, int height)
{
struct rdp_output *output = to_rdp_output(base);
+ struct weston_head *head = &output->base.head;
struct weston_mode *currentMode;
struct weston_mode initMode;
@@ -500,12 +501,11 @@ rdp_output_set_size(struct weston_output *base,
return -1;
output->base.current_mode = output->base.native_mode = currentMode;
- output->base.make = "weston";
- output->base.model = "rdp";
+
+ weston_head_set_monitor_strings(head, "weston", "rdp", NULL);
/* XXX: Calculate proper size. */
- output->base.mm_width = width;
- output->base.mm_height = height;
+ weston_head_set_physical_size(head, width, height);
output->base.start_repaint_loop = rdp_output_start_repaint_loop;
output->base.repaint = rdp_output_repaint;
diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c
index 3bdfb03e..040c40b9 100644
--- a/libweston/compositor-wayland.c
+++ b/libweston/compositor-wayland.c
@@ -1313,6 +1313,7 @@ static int
wayland_output_set_size(struct weston_output *base, int width, int height)
{
struct wayland_output *output = to_wayland_output(base);
+ struct weston_head *head = &output->base.head;
int output_width, output_height;
/* We can only be called once. */
@@ -1345,12 +1346,11 @@ wayland_output_set_size(struct weston_output *base, int width, int height)
wl_list_insert(&output->base.mode_list, &output->mode.link);
output->base.current_mode = &output->mode;
- output->base.make = "wayland";
- output->base.model = "none";
+
+ weston_head_set_monitor_strings(head, "wayland", "none", NULL);
/* XXX: Calculate proper size. */
- output->base.mm_width = width;
- output->base.mm_height = height;
+ weston_head_set_physical_size(head, width, height);
return 0;
}
@@ -1383,10 +1383,12 @@ wayland_output_create_for_parent_output(struct wayland_backend *b,
output->parent.output = poutput->global;
- output->base.make = poutput->physical.make;
- output->base.model = poutput->physical.model;
- output->base.mm_width = poutput->physical.width;
- output->base.mm_height = poutput->physical.height;
+ weston_head_set_monitor_strings(&output->base.head,
+ poutput->physical.make,
+ poutput->physical.model, NULL);
+ weston_head_set_physical_size(&output->base.head,
+ poutput->physical.width,
+ poutput->physical.height);
wl_list_insert_list(&output->base.mode_list, &poutput->mode_list);
wl_list_init(&poutput->mode_list);
diff --git a/libweston/compositor-x11.c b/libweston/compositor-x11.c
index fd948540..b800132a 100644
--- a/libweston/compositor-x11.c
+++ b/libweston/compositor-x11.c
@@ -1061,6 +1061,8 @@ x11_output_set_size(struct weston_output *base, int width, int height)
{
struct x11_output *output = to_x11_output(base);
struct x11_backend *b = to_x11_backend(base->compositor);
+ struct weston_head *head = &output->base.head;
+ xcb_screen_t *scrn = b->screen;
int output_width, output_height;
/* We can only be called once. */
@@ -1095,16 +1097,13 @@ x11_output_set_size(struct weston_output *base, int width, int height)
wl_list_insert(&output->base.mode_list, &output->mode.link);
output->base.current_mode = &output->mode;
- output->base.make = "weston-X11";
- output->base.model = "none";
-
output->base.native_mode = &output->native;
output->base.native_scale = output->base.scale;
- output->base.mm_width = width * b->screen->width_in_millimeters /
- b->screen->width_in_pixels;
- output->base.mm_height = height * b->screen->height_in_millimeters /
- b->screen->height_in_pixels;
+ weston_head_set_monitor_strings(head, "weston-X11", "none", NULL);
+ weston_head_set_physical_size(head,
+ width * scrn->width_in_millimeters / scrn->width_in_pixels,
+ height * scrn->height_in_millimeters / scrn->height_in_pixels);
return 0;
}
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 7d7a17ed..04ac65d2 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4313,6 +4313,7 @@ bind_output(struct wl_client *client,
struct weston_output *output = data;
struct weston_mode *mode;
struct wl_resource *resource;
+ struct weston_head *head = &output->head;
resource = wl_resource_create(client, &wl_output_interface,
version, id);
@@ -4327,10 +4328,10 @@ bind_output(struct wl_client *client,
wl_output_send_geometry(resource,
output->x,
output->y,
- output->mm_width,
- output->mm_height,
- output->subpixel,
- output->make, output->model,
+ head->mm_width,
+ head->mm_height,
+ head->subpixel,
+ head->make, head->model,
output->transform);
if (version >= WL_OUTPUT_SCALE_SINCE_VERSION)
wl_output_send_scale(resource,
@@ -4363,6 +4364,85 @@ weston_output_from_resource(struct wl_resource *resource)
return wl_resource_get_user_data(resource);
}
+/** Store monitor make, model and serial number
+ *
+ * \param head The head to modify.
+ * \param make The monitor make. If EDID is available, the PNP ID. Otherwise
+ * any string, or NULL for none.
+ * \param model The monitor model or name, or a made-up string, or NULL for
+ * none.
+ * \param serialno The monitor serial number, a made-up string, or NULL for
+ * none.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_monitor_strings(struct weston_head *head,
+ const char *make,
+ const char *model,
+ const char *serialno)
+{
+ head->make = (char *)make;
+ head->model = (char *)model;
+ head->serial_number = (char *)serialno;
+}
+
+/** Store physical image size
+ *
+ * \param head The head to modify.
+ * \param mm_width Image area width in millimeters.
+ * \param mm_height Image area height in millimeters.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_physical_size(struct weston_head *head,
+ int32_t mm_width, int32_t mm_height)
+{
+ head->mm_width = mm_width;
+ head->mm_height = mm_height;
+}
+
+/** Store monitor sub-pixel layout
+ *
+ * \param head The head to modify.
+ * - WL_OUTPUT_SUBPIXEL_UNKNOWN,
+ * - WL_OUTPUT_SUBPIXEL_NONE,
+ * - WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB,
+ * - WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR,
+ * - WL_OUTPUT_SUBPIXEL_VERTICAL_RGB,
+ * - WL_OUTPUT_SUBPIXEL_VERTICAL_BGR
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_subpixel(struct weston_head *head,
+ enum wl_output_subpixel sp)
+{
+ head->subpixel = sp;
+}
+
+/** Mark the monitor as internal
+ *
+ * This is used for embedded screens, like laptop panels.
+ *
+ * \param head The head to mark as internal.
+ *
+ * By default a head is external. The type is often inferred from the physical
+ * connector type.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_internal(struct weston_head *head)
+{
+ head->connection_internal = true;
+}
/* Move other outputs when one is resized so the space remains contiguous. */
static void
@@ -4479,6 +4559,7 @@ weston_output_init_geometry(struct weston_output *output, int x, int y)
WL_EXPORT void
weston_output_move(struct weston_output *output, int x, int y)
{
+ struct weston_head *head = &output->head;
struct wl_resource *resource;
output->move_x = x - output->x;
@@ -4499,11 +4580,11 @@ weston_output_move(struct weston_output *output, int x, int y)
wl_output_send_geometry(resource,
output->x,
output->y,
- output->mm_width,
- output->mm_height,
- output->subpixel,
- output->make,
- output->model,
+ head->mm_width,
+ head->mm_height,
+ head->subpixel,
+ head->make,
+ head->model,
output->transform);
if (wl_resource_get_version(resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
@@ -4721,6 +4802,8 @@ weston_output_init(struct weston_output *output,
struct weston_compositor *compositor,
const char *name)
{
+ struct weston_head *head = &output->head;
+
output->compositor = compositor;
output->destroying = 0;
output->name = strdup(name);
@@ -4730,8 +4813,8 @@ weston_output_init(struct weston_output *output,
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
*/
- output->mm_width = 0;
- output->mm_height = 0;
+ head->mm_width = 0;
+ head->mm_height = 0;
output->scale = 0;
/* Can't use -1 on uint32_t and 0 is valid enum value */
output->transform = UINT32_MAX;
diff --git a/libweston/compositor.h b/libweston/compositor.h
index dffcba89..b1bed44c 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -147,6 +147,21 @@ enum dpms_enum {
WESTON_DPMS_OFF
};
+/** Represents a monitor
+ *
+ * This object represents a monitor (hardware backends like DRM) or a window
+ * (windowed nested backends).
+ */
+struct weston_head {
+ int32_t mm_width; /**< physical image width in mm */
+ int32_t mm_height; /**< physical image height in mm */
+ char *make; /**< monitor manufacturer (PNP ID) */
+ char *model; /**< monitor model */
+ char *serial_number; /**< monitor serial */
+ uint32_t subpixel; /**< enum wl_output_subpixel */
+ bool connection_internal; /**< embedded monitor (e.g. laptop) */
+};
+
struct weston_output {
uint32_t id;
char *name;
@@ -165,7 +180,6 @@ struct weston_output {
struct wl_list animation_list;
int32_t x, y, width, height;
- int32_t mm_width, mm_height;
/** Output area in global coordinates, simple rect */
pixman_region32_t region;
@@ -199,8 +213,6 @@ struct weston_output {
int destroying;
struct wl_list feedback_list;
- char *make, *model, *serial_number;
- uint32_t subpixel;
uint32_t transform;
int32_t native_scale;
int32_t current_scale;
@@ -211,6 +223,8 @@ struct weston_output {
struct weston_mode *original_mode;
struct wl_list mode_list;
+ struct weston_head head;
+
void (*start_repaint_loop)(struct weston_output *output);
int (*repaint)(struct weston_output *output,
pixman_region32_t *damage,
@@ -224,7 +238,6 @@ struct weston_output {
void (*set_backlight)(struct weston_output *output, uint32_t value);
void (*set_dpms)(struct weston_output *output, enum dpms_enum level);
- bool connection_internal;
uint16_t gamma_size;
void (*set_gamma)(struct weston_output *output,
uint16_t size,
@@ -1932,6 +1945,23 @@ int
weston_compositor_load_xwayland(struct weston_compositor *compositor);
void
+weston_head_set_monitor_strings(struct weston_head *head,
+ const char *make,
+ const char *model,
+ const char *serialno);
I was going to complain about the apparently silly trivial helper
functions, but their importance becomes obvious later in the series.

Reviewed-by: Derek Foreman <***@osg.samsung.com>
(though I don't want to see any of it land until we can get as far as
removing the embedded weston_head in weston_output...)

Thanks,
Derek
Post by Pekka Paalanen
+
+void
+weston_head_set_physical_size(struct weston_head *head,
+ int32_t mm_width, int32_t mm_height);
+
+void
+weston_head_set_subpixel(struct weston_head *head,
+ enum wl_output_subpixel sp);
+
+void
+weston_head_set_internal(struct weston_head *head);
+
+void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
Pekka Paalanen
2018-02-02 08:06:26 UTC
Permalink
On Thu, 1 Feb 2018 13:59:24 -0600
Post by Derek Foreman
Post by Pekka Paalanen
In order to support clone modes, libweston needs the concept of a head
that is separate from weston_output. While weston_output manages buffers
and the repaint state machine, weston_head will represent a single
monitor. In the future it will be possible to have a single
weston_output drive one or more weston_heads for a clone mode that
shares the framebuffers between all cloned heads.
All the fields that are obviously properties of the monitor are moved
from weston_output into weston_head.
As moving the fields requires one to touch all the backends for all the
assingments, introduce setter functions for them while we are here. The
setters are identical to the old assignments, for now.
As a temporary measure, weston_output embeds a single head. Also the
ugly casts in weston_head_set_monitor_strings() will be removed by a
follow-up patch.
---
compositor/cms-colord.c | 32 ++++++------
libweston/compositor-drm.c | 15 +++---
libweston/compositor-fbdev.c | 12 +++--
libweston/compositor-headless.c | 8 +--
libweston/compositor-rdp.c | 8 +--
libweston/compositor-wayland.c | 18 ++++---
libweston/compositor-x11.c | 13 +++--
libweston/compositor.c | 105 +++++++++++++++++++++++++++++++++++-----
libweston/compositor.h | 38 +++++++++++++--
9 files changed, 184 insertions(+), 65 deletions(-)
void
+weston_head_set_monitor_strings(struct weston_head *head,
+ const char *make,
+ const char *model,
+ const char *serialno);
I was going to complain about the apparently silly trivial helper
functions, but their importance becomes obvious later in the series.
Right, and another reason is to pave way for making structs opaque, as
will be necessary for cleaning up the libweston ABI. Well, the setters
are for backends which would be fine having the struct definitions
visible, but I think it fits anyway.

Moving things towards a more proper libweston ABI is an idea carried
throughout the patch series.
Post by Derek Foreman
(though I don't want to see any of it land until we can get as far as
removing the embedded weston_head in weston_output...)
Very good. We need to get the atomic series landed then, before I can
rebase this series and include the drm-backend migration which is
required for removing the embedded head.


Thanks,
pq
Derek Foreman
2018-02-02 18:56:47 UTC
Permalink
Post by Pekka Paalanen
On Thu, 1 Feb 2018 13:59:24 -0600
Post by Derek Foreman
Post by Pekka Paalanen
In order to support clone modes, libweston needs the concept of a head
that is separate from weston_output. While weston_output manages buffers
and the repaint state machine, weston_head will represent a single
monitor. In the future it will be possible to have a single
weston_output drive one or more weston_heads for a clone mode that
shares the framebuffers between all cloned heads.
All the fields that are obviously properties of the monitor are moved
from weston_output into weston_head.
As moving the fields requires one to touch all the backends for all the
assingments, introduce setter functions for them while we are here. The
setters are identical to the old assignments, for now.
As a temporary measure, weston_output embeds a single head. Also the
ugly casts in weston_head_set_monitor_strings() will be removed by a
follow-up patch.
---
compositor/cms-colord.c | 32 ++++++------
libweston/compositor-drm.c | 15 +++---
libweston/compositor-fbdev.c | 12 +++--
libweston/compositor-headless.c | 8 +--
libweston/compositor-rdp.c | 8 +--
libweston/compositor-wayland.c | 18 ++++---
libweston/compositor-x11.c | 13 +++--
libweston/compositor.c | 105 +++++++++++++++++++++++++++++++++++-----
libweston/compositor.h | 38 +++++++++++++--
9 files changed, 184 insertions(+), 65 deletions(-)
void
+weston_head_set_monitor_strings(struct weston_head *head,
+ const char *make,
+ const char *model,
+ const char *serialno);
I was going to complain about the apparently silly trivial helper
functions, but their importance becomes obvious later in the series.
Right, and another reason is to pave way for making structs opaque, as
will be necessary for cleaning up the libweston ABI. Well, the setters
are for backends which would be fine having the struct definitions
visible, but I think it fits anyway.
Moving things towards a more proper libweston ABI is an idea carried
throughout the patch series.
Post by Derek Foreman
(though I don't want to see any of it land until we can get as far as
removing the embedded weston_head in weston_output...)
Very good. We need to get the atomic series landed then, before I can
rebase this series and include the drm-backend migration which is
required for removing the embedded head.
I'm vacillating on this point. Atomic has seen so many revisions and
delays that at this point making something dependent on it landing first
seems unusually cruel.

weston_head being embedded in weston_output isn't even all that ugly as
a refactor, ignoring it as a requisite step to get to clone mode.

I guess we'll consider this again in a few days.

Thanks,
Derek
Post by Pekka Paalanen
Thanks,
pq
Pekka Paalanen
2017-12-14 11:40:39 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The wl_output protocol interface exposes things like monitor make,
model, sub-pixel layout and physical dimensions. Obviously wl_output is
meant to represent a monitor.

The abstraction of a monitor is weston_head. Therefore move the wl_output
global and the bound resources list into weston_head.

When clone mode gets implemented in the future, this means that monitors
driven by the same CRTC will still be represented as separate wl_output
globals. This allows to accurately represent the hardware.

Clone mode that used separate, not frame-locked, CRTCs to drive two
monitors as clones would necessarily also be exposed as separate
wl_output since they have different timings.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 39 ++++++++++++++++++++++++---------------
libweston/compositor.h | 5 +++--
2 files changed, 27 insertions(+), 17 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 04ac65d2..9f1c6d83 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -81,6 +81,7 @@ static void weston_mode_switch_finish(struct weston_output *output,
int scale_changed)
{
struct weston_seat *seat;
+ struct weston_head *head;
struct wl_resource *resource;
pixman_region32_t old_output_region;
int version;
@@ -129,8 +130,10 @@ static void weston_mode_switch_finish(struct weston_output *output,
if (!mode_changed && !scale_changed)
return;

+ head = &output->head;
+
/* notify clients of the changes */
- wl_resource_for_each(resource, &output->resource_list) {
+ wl_resource_for_each(resource, &head->resource_list) {
if (mode_changed) {
wl_output_send_mode(resource,
output->current_mode->flags,
@@ -340,10 +343,12 @@ weston_presentation_feedback_present(
uint32_t flags)
{
struct wl_client *client = wl_resource_get_client(feedback->resource);
+ struct weston_head *head;
struct wl_resource *o;
uint64_t secs;

- wl_resource_for_each(o, &output->resource_list) {
+ head = &output->head;
+ wl_resource_for_each(o, &head->resource_list) {
if (wl_resource_get_client(o) != client)
continue;

@@ -921,7 +926,7 @@ weston_view_damage_below(struct weston_view *view)
/** Send wl_surface.enter/leave events
*
* \param surface The surface.
- * \param output The entered/left output.
+ * \param head A head of the entered/left output.
* \param enter True if entered.
* \param left True if left.
*
@@ -930,7 +935,7 @@ weston_view_damage_below(struct weston_view *view)
*/
static void
weston_surface_send_enter_leave(struct weston_surface *surface,
- struct weston_output *output,
+ struct weston_head *head,
bool enter,
bool leave)
{
@@ -940,7 +945,7 @@ weston_surface_send_enter_leave(struct weston_surface *surface,
assert(enter != leave);

client = wl_resource_get_client(surface->resource);
- wl_resource_for_each(wloutput, &output->resource_list) {
+ wl_resource_for_each(wloutput, &head->resource_list) {
if (wl_resource_get_client(wloutput) != client)
continue;

@@ -980,7 +985,7 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
if (!(output_bit & different))
continue;

- weston_surface_send_enter_leave(es, output,
+ weston_surface_send_enter_leave(es, &output->head,
output_bit & entered,
output_bit & left);
}
@@ -4322,7 +4327,7 @@ bind_output(struct wl_client *client,
return;
}

- wl_list_insert(&output->resource_list, wl_resource_get_link(resource));
+ wl_list_insert(&head->resource_list, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &output_interface, data, unbind_resource);

wl_output_send_geometry(resource,
@@ -4576,7 +4581,7 @@ weston_output_move(struct weston_output *output, int x, int y)
wl_signal_emit(&output->compositor->output_moved_signal, output);

/* Notify clients of the change for output position. */
- wl_resource_for_each(resource, &output->resource_list) {
+ wl_resource_for_each(resource, &head->resource_list) {
wl_output_send_geometry(resource,
output->x,
output->y,
@@ -4610,6 +4615,7 @@ weston_compositor_add_output(struct weston_compositor *compositor,
struct weston_output *output)
{
struct weston_view *view, *next;
+ struct weston_head *head;

assert(!output->enabled);

@@ -4627,9 +4633,10 @@ weston_compositor_add_output(struct weston_compositor *compositor,
wl_list_insert(compositor->output_list.prev, &output->link);
output->enabled = true;

- output->global = wl_global_create(compositor->wl_display,
- &wl_output_interface, 3,
- output, bind_output);
+ head = &output->head;
+ head->global = wl_global_create(compositor->wl_display,
+ &wl_output_interface, 3,
+ output, bind_output);

wl_signal_emit(&compositor->output_created_signal, output);

@@ -4701,6 +4708,7 @@ weston_compositor_remove_output(struct weston_output *output)
struct weston_compositor *compositor = output->compositor;
struct wl_resource *resource;
struct weston_view *view;
+ struct weston_head *head;

assert(output->destroying);
assert(output->enabled);
@@ -4721,9 +4729,10 @@ weston_compositor_remove_output(struct weston_output *output)
wl_signal_emit(&compositor->output_destroyed_signal, output);
wl_signal_emit(&output->destroy_signal, output);

- wl_global_destroy(output->global);
- output->global = NULL;
- wl_resource_for_each(resource, &output->resource_list) {
+ head = &output->head;
+ wl_global_destroy(head->global);
+ head->global = NULL;
+ wl_resource_for_each(resource, &head->resource_list) {
wl_resource_set_destructor(resource, NULL);
}

@@ -4921,7 +4930,7 @@ weston_output_enable(struct weston_output *output)
wl_signal_init(&output->frame_signal);
wl_signal_init(&output->destroy_signal);
wl_list_init(&output->animation_list);
- wl_list_init(&output->resource_list);
+ wl_list_init(&output->head.resource_list);
wl_list_init(&output->feedback_list);

/* Enable the output (set up the crtc or create a
diff --git a/libweston/compositor.h b/libweston/compositor.h
index b1bed44c..76b966ee 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -153,6 +153,9 @@ enum dpms_enum {
* (windowed nested backends).
*/
struct weston_head {
+ struct wl_list resource_list; /**< wl_output protocol objects */
+ struct wl_global *global; /**< wl_output global */
+
int32_t mm_width; /**< physical image width in mm */
int32_t mm_height; /**< physical image height in mm */
char *make; /**< monitor manufacturer (PNP ID) */
@@ -169,8 +172,6 @@ struct weston_output {
void *renderer_state;

struct wl_list link;
- struct wl_list resource_list;
- struct wl_global *global;
struct weston_compositor *compositor;

/** From global to output buffer coordinates. */
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:40 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

As a wl_output represents weston_head, use a weston_head pointer as the
wl_output global's user data.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 11 +++++++----
libweston/compositor.h | 2 ++
2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 9f1c6d83..5be9e91e 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4315,10 +4315,10 @@ static void
bind_output(struct wl_client *client,
void *data, uint32_t version, uint32_t id)
{
- struct weston_output *output = data;
+ struct weston_head *head = data;
+ struct weston_output *output = head->output;
struct weston_mode *mode;
struct wl_resource *resource;
- struct weston_head *head = &output->head;

resource = wl_resource_create(client, &wl_output_interface,
version, id);
@@ -4328,8 +4328,10 @@ bind_output(struct wl_client *client,
}

wl_list_insert(&head->resource_list, wl_resource_get_link(resource));
- wl_resource_set_implementation(resource, &output_interface, data, unbind_resource);
+ wl_resource_set_implementation(resource, &output_interface, output,
+ unbind_resource);

+ assert(output);
wl_output_send_geometry(resource,
output->x,
output->y,
@@ -4634,9 +4636,10 @@ weston_compositor_add_output(struct weston_compositor *compositor,
output->enabled = true;

head = &output->head;
+ head->output = output;
head->global = wl_global_create(compositor->wl_display,
&wl_output_interface, 3,
- output, bind_output);
+ head, bind_output);

wl_signal_emit(&compositor->output_created_signal, output);

diff --git a/libweston/compositor.h b/libweston/compositor.h
index 76b966ee..cc74bfc9 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -153,6 +153,8 @@ enum dpms_enum {
* (windowed nested backends).
*/
struct weston_head {
+ struct weston_output *output; /**< the output driving this head */
+
struct wl_list resource_list; /**< wl_output protocol objects */
struct wl_global *global; /**< wl_output global */
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:41 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The user data of a wl_resource representing a wl_output protocol object
used to be a pointer to weston_output. Now that weston_output is being
split, wl_output more accurately refers to weston_head which is a single
monitor.

Change the wl_output user data to point to weston_head.
weston_output_from_resource() is replaced with
weston_head_from_resource().

This change is not strictly necessary, but architecturally it is the
right thing to do. In the future there might appear the need to refer to
a specific head of a cloned pair, for instance.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/weston-screenshooter.c | 2 +-
desktop-shell/input-panel.c | 4 +++-
desktop-shell/shell.c | 4 ++--
fullscreen-shell/fullscreen-shell.c | 4 ++--
ivi-shell/input-panel-ivi.c | 4 +++-
libweston-desktop/wl-shell.c | 2 +-
libweston-desktop/xdg-shell-v5.c | 2 +-
libweston-desktop/xdg-shell-v6.c | 2 +-
libweston/compositor.c | 6 +++---
libweston/compositor.h | 4 ++--
tests/weston-test.c | 2 +-
11 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/compositor/weston-screenshooter.c b/compositor/weston-screenshooter.c
index f0bc0e1e..70afed4a 100644
--- a/compositor/weston-screenshooter.c
+++ b/compositor/weston-screenshooter.c
@@ -66,7 +66,7 @@ screenshooter_shoot(struct wl_client *client,
struct wl_resource *buffer_resource)
{
struct weston_output *output =
- weston_output_from_resource(output_resource);
+ weston_head_from_resource(output_resource)->output;
struct weston_buffer *buffer =
weston_buffer_from_resource(buffer_resource);

diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c
index e6b1541a..8292f20a 100644
--- a/desktop-shell/input-panel.c
+++ b/desktop-shell/input-panel.c
@@ -270,11 +270,13 @@ input_panel_surface_set_toplevel(struct wl_client *client,
struct input_panel_surface *input_panel_surface =
wl_resource_get_user_data(resource);
struct desktop_shell *shell = input_panel_surface->shell;
+ struct weston_head *head;

wl_list_insert(&shell->input_panel.surfaces,
&input_panel_surface->link);

- input_panel_surface->output = weston_output_from_resource(output_resource);
+ head = weston_head_from_resource(output_resource);
+ input_panel_surface->output = head->output;
input_panel_surface->panel = 0;
}

diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index a2a93e2f..40953990 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -2968,7 +2968,7 @@ desktop_shell_set_background(struct wl_client *client,
surface->committed = background_committed;
surface->committed_private = shell;
weston_surface_set_label_func(surface, background_get_label);
- surface->output = weston_output_from_resource(output_resource);
+ surface->output = weston_head_from_resource(output_resource)->output;
view->output = surface->output;
weston_desktop_shell_send_configure(resource, 0,
surface_resource,
@@ -3053,7 +3053,7 @@ desktop_shell_set_panel(struct wl_client *client,
surface->committed = panel_committed;
surface->committed_private = shell;
weston_surface_set_label_func(surface, panel_get_label);
- surface->output = weston_output_from_resource(output_resource);
+ surface->output = weston_head_from_resource(output_resource)->output;
view->output = surface->output;
weston_desktop_shell_send_configure(resource, 0,
surface_resource,
diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c
index 6f4565a7..89884794 100644
--- a/fullscreen-shell/fullscreen-shell.c
+++ b/fullscreen-shell/fullscreen-shell.c
@@ -769,7 +769,7 @@ fullscreen_shell_present_surface(struct wl_client *client,
}

if (output_res) {
- output = weston_output_from_resource(output_res);
+ output = weston_head_from_resource(output_res)->output;
fsout = fs_output_for_output(output);
fs_output_set_surface(fsout, surface, method, 0, 0);
} else {
@@ -813,7 +813,7 @@ fullscreen_shell_present_surface_for_mode(struct wl_client *client,
struct weston_seat *seat;
struct fs_output *fsout;

- output = weston_output_from_resource(output_res);
+ output = weston_head_from_resource(output_res)->output;
fsout = fs_output_for_output(output);

if (surface_res == NULL) {
diff --git a/ivi-shell/input-panel-ivi.c b/ivi-shell/input-panel-ivi.c
index 0008a52d..219494dc 100644
--- a/ivi-shell/input-panel-ivi.c
+++ b/ivi-shell/input-panel-ivi.c
@@ -271,11 +271,13 @@ input_panel_surface_set_toplevel(struct wl_client *client,
struct input_panel_surface *input_panel_surface =
wl_resource_get_user_data(resource);
struct ivi_shell *shell = input_panel_surface->shell;
+ struct weston_head *head;

wl_list_insert(&shell->input_panel.surfaces,
&input_panel_surface->link);

- input_panel_surface->output = weston_output_from_resource(output_resource);
+ head = weston_head_from_resource(output_resource);
+ input_panel_surface->output = head->output;
input_panel_surface->panel = 0;
}

diff --git a/libweston-desktop/wl-shell.c b/libweston-desktop/wl-shell.c
index 66553f45..0221dc2f 100644
--- a/libweston-desktop/wl-shell.c
+++ b/libweston-desktop/wl-shell.c
@@ -302,7 +302,7 @@ weston_desktop_wl_shell_surface_protocol_set_fullscreen(struct wl_client *wl_cli
struct weston_output *output = NULL;

if (output_resource != NULL)
- output = weston_output_from_resource(output_resource);
+ output = weston_head_from_resource(output_resource)->output;

weston_desktop_wl_shell_change_state(surface, FULLSCREEN, NULL, 0, 0);
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface,
diff --git a/libweston-desktop/xdg-shell-v5.c b/libweston-desktop/xdg-shell-v5.c
index ebe7940e..188d1ca6 100644
--- a/libweston-desktop/xdg-shell-v5.c
+++ b/libweston-desktop/xdg-shell-v5.c
@@ -559,7 +559,7 @@ weston_desktop_xdg_surface_protocol_set_fullscreen(struct wl_client *wl_client,
struct weston_output *output = NULL;

if (output_resource != NULL)
- output = weston_output_from_resource(output_resource);
+ output = weston_head_from_resource(output_resource)->output;

weston_desktop_xdg_surface_ensure_added(surface);
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface,
diff --git a/libweston-desktop/xdg-shell-v6.c b/libweston-desktop/xdg-shell-v6.c
index 4db3748b..f960e67f 100644
--- a/libweston-desktop/xdg-shell-v6.c
+++ b/libweston-desktop/xdg-shell-v6.c
@@ -506,7 +506,7 @@ weston_desktop_xdg_toplevel_protocol_set_fullscreen(struct wl_client *wl_client,
struct weston_output *output = NULL;

if (output_resource != NULL)
- output = weston_output_from_resource(output_resource);
+ output = weston_head_from_resource(output_resource)->output;

weston_desktop_xdg_toplevel_ensure_added(toplevel);
weston_desktop_api_fullscreen_requested(toplevel->base.desktop, dsurface,
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 5be9e91e..66453ea3 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4328,7 +4328,7 @@ bind_output(struct wl_client *client,
}

wl_list_insert(&head->resource_list, wl_resource_get_link(resource));
- wl_resource_set_implementation(resource, &output_interface, output,
+ wl_resource_set_implementation(resource, &output_interface, head,
unbind_resource);

assert(output);
@@ -4362,8 +4362,8 @@ bind_output(struct wl_client *client,
* \return The backing object (user data) of a wl_resource representing a
* wl_output protocol object.
*/
-WL_EXPORT struct weston_output *
-weston_output_from_resource(struct wl_resource *resource)
+WL_EXPORT struct weston_head *
+weston_head_from_resource(struct wl_resource *resource)
{
assert(wl_resource_instance_of(resource, &wl_output_interface,
&output_interface));
diff --git a/libweston/compositor.h b/libweston/compositor.h
index cc74bfc9..5e0a2867 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1990,8 +1990,8 @@ weston_output_disable(struct weston_output *output);
void
weston_pending_output_coldplug(struct weston_compositor *compositor);

-struct weston_output *
-weston_output_from_resource(struct wl_resource *resource);
+struct weston_head *
+weston_head_from_resource(struct wl_resource *resource);

#ifdef __cplusplus
}
diff --git a/tests/weston-test.c b/tests/weston-test.c
index 6e7beeb7..8bbc2089 100644
--- a/tests/weston-test.c
+++ b/tests/weston-test.c
@@ -484,7 +484,7 @@ capture_screenshot(struct wl_client *client,
struct wl_resource *buffer_resource)
{
struct weston_output *output =
- weston_output_from_resource(output_resource);
+ weston_head_from_resource(output_resource)->output;
struct weston_buffer *buffer =
weston_buffer_from_resource(buffer_resource);
--
2.13.6
Derek Foreman
2018-02-01 20:10:16 UTC
Permalink
Post by Pekka Paalanen
The user data of a wl_resource representing a wl_output protocol object
used to be a pointer to weston_output. Now that weston_output is being
split, wl_output more accurately refers to weston_head which is a single
monitor.
Change the wl_output user data to point to weston_head.
weston_output_from_resource() is replaced with
weston_head_from_resource().
This change is not strictly necessary, but architecturally it is the
right thing to do. In the future there might appear the need to refer to
a specific head of a cloned pair, for instance.
---
compositor/weston-screenshooter.c | 2 +-
desktop-shell/input-panel.c | 4 +++-
desktop-shell/shell.c | 4 ++--
fullscreen-shell/fullscreen-shell.c | 4 ++--
ivi-shell/input-panel-ivi.c | 4 +++-
libweston-desktop/wl-shell.c | 2 +-
libweston-desktop/xdg-shell-v5.c | 2 +-
libweston-desktop/xdg-shell-v6.c | 2 +-
libweston/compositor.c | 6 +++---
libweston/compositor.h | 4 ++--
tests/weston-test.c | 2 +-
11 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/compositor/weston-screenshooter.c b/compositor/weston-screenshooter.c
index f0bc0e1e..70afed4a 100644
--- a/compositor/weston-screenshooter.c
+++ b/compositor/weston-screenshooter.c
@@ -66,7 +66,7 @@ screenshooter_shoot(struct wl_client *client,
struct wl_resource *buffer_resource)
{
struct weston_output *output =
- weston_output_from_resource(output_resource);
+ weston_head_from_resource(output_resource)->output;
struct weston_buffer *buffer =
weston_buffer_from_resource(buffer_resource);
diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c
index e6b1541a..8292f20a 100644
--- a/desktop-shell/input-panel.c
+++ b/desktop-shell/input-panel.c
@@ -270,11 +270,13 @@ input_panel_surface_set_toplevel(struct wl_client *client,
struct input_panel_surface *input_panel_surface =
wl_resource_get_user_data(resource);
struct desktop_shell *shell = input_panel_surface->shell;
+ struct weston_head *head;
wl_list_insert(&shell->input_panel.surfaces,
&input_panel_surface->link);
- input_panel_surface->output = weston_output_from_resource(output_resource);
+ head = weston_head_from_resource(output_resource);
+ input_panel_surface->output = head->output;
input_panel_surface->panel = 0;
}
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index a2a93e2f..40953990 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -2968,7 +2968,7 @@ desktop_shell_set_background(struct wl_client *client,
surface->committed = background_committed;
surface->committed_private = shell;
weston_surface_set_label_func(surface, background_get_label);
- surface->output = weston_output_from_resource(output_resource);
+ surface->output = weston_head_from_resource(output_resource)->output;
view->output = surface->output;
weston_desktop_shell_send_configure(resource, 0,
surface_resource,
@@ -3053,7 +3053,7 @@ desktop_shell_set_panel(struct wl_client *client,
surface->committed = panel_committed;
surface->committed_private = shell;
weston_surface_set_label_func(surface, panel_get_label);
- surface->output = weston_output_from_resource(output_resource);
+ surface->output = weston_head_from_resource(output_resource)->output;
view->output = surface->output;
weston_desktop_shell_send_configure(resource, 0,
surface_resource,
diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c
index 6f4565a7..89884794 100644
--- a/fullscreen-shell/fullscreen-shell.c
+++ b/fullscreen-shell/fullscreen-shell.c
@@ -769,7 +769,7 @@ fullscreen_shell_present_surface(struct wl_client *client,
}
if (output_res) {
- output = weston_output_from_resource(output_res);
+ output = weston_head_from_resource(output_res)->output;
fsout = fs_output_for_output(output);
fs_output_set_surface(fsout, surface, method, 0, 0);
} else {
@@ -813,7 +813,7 @@ fullscreen_shell_present_surface_for_mode(struct wl_client *client,
struct weston_seat *seat;
struct fs_output *fsout;
- output = weston_output_from_resource(output_res);
+ output = weston_head_from_resource(output_res)->output;
fsout = fs_output_for_output(output);
if (surface_res == NULL) {
diff --git a/ivi-shell/input-panel-ivi.c b/ivi-shell/input-panel-ivi.c
index 0008a52d..219494dc 100644
--- a/ivi-shell/input-panel-ivi.c
+++ b/ivi-shell/input-panel-ivi.c
@@ -271,11 +271,13 @@ input_panel_surface_set_toplevel(struct wl_client *client,
struct input_panel_surface *input_panel_surface =
wl_resource_get_user_data(resource);
struct ivi_shell *shell = input_panel_surface->shell;
+ struct weston_head *head;
wl_list_insert(&shell->input_panel.surfaces,
&input_panel_surface->link);
- input_panel_surface->output = weston_output_from_resource(output_resource);
+ head = weston_head_from_resource(output_resource);
+ input_panel_surface->output = head->output;
I wanted to complain about the apparent inconsistency here, however
repeating the weston_head_from_resource(output_resource)->output idiom
here would've landed at 90 cols.
Post by Pekka Paalanen
input_panel_surface->panel = 0;
}
diff --git a/libweston-desktop/wl-shell.c b/libweston-desktop/wl-shell.c
index 66553f45..0221dc2f 100644
--- a/libweston-desktop/wl-shell.c
+++ b/libweston-desktop/wl-shell.c
@@ -302,7 +302,7 @@ weston_desktop_wl_shell_surface_protocol_set_fullscreen(struct wl_client *wl_cli
struct weston_output *output = NULL;
if (output_resource != NULL)
- output = weston_output_from_resource(output_resource);
+ output = weston_head_from_resource(output_resource)->output;
weston_desktop_wl_shell_change_state(surface, FULLSCREEN, NULL, 0, 0);
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface,
diff --git a/libweston-desktop/xdg-shell-v5.c b/libweston-desktop/xdg-shell-v5.c
index ebe7940e..188d1ca6 100644
--- a/libweston-desktop/xdg-shell-v5.c
+++ b/libweston-desktop/xdg-shell-v5.c
@@ -559,7 +559,7 @@ weston_desktop_xdg_surface_protocol_set_fullscreen(struct wl_client *wl_client,
struct weston_output *output = NULL;
if (output_resource != NULL)
- output = weston_output_from_resource(output_resource);
+ output = weston_head_from_resource(output_resource)->output;
weston_desktop_xdg_surface_ensure_added(surface);
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface,
diff --git a/libweston-desktop/xdg-shell-v6.c b/libweston-desktop/xdg-shell-v6.c
index 4db3748b..f960e67f 100644
--- a/libweston-desktop/xdg-shell-v6.c
+++ b/libweston-desktop/xdg-shell-v6.c
@@ -506,7 +506,7 @@ weston_desktop_xdg_toplevel_protocol_set_fullscreen(struct wl_client *wl_client,
struct weston_output *output = NULL;
if (output_resource != NULL)
- output = weston_output_from_resource(output_resource);
+ output = weston_head_from_resource(output_resource)->output;
weston_desktop_xdg_toplevel_ensure_added(toplevel);
weston_desktop_api_fullscreen_requested(toplevel->base.desktop, dsurface,
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 5be9e91e..66453ea3 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4328,7 +4328,7 @@ bind_output(struct wl_client *client,
}
wl_list_insert(&head->resource_list, wl_resource_get_link(resource));
- wl_resource_set_implementation(resource, &output_interface, output,
+ wl_resource_set_implementation(resource, &output_interface, head,
unbind_resource);
assert(output);
@@ -4362,8 +4362,8 @@ bind_output(struct wl_client *client,
* \return The backing object (user data) of a wl_resource representing a
* wl_output protocol object.
*/
-WL_EXPORT struct weston_output *
-weston_output_from_resource(struct wl_resource *resource)
+WL_EXPORT struct weston_head *
+weston_head_from_resource(struct wl_resource *resource)
{
assert(wl_resource_instance_of(resource, &wl_output_interface,
&output_interface));
diff --git a/libweston/compositor.h b/libweston/compositor.h
index cc74bfc9..5e0a2867 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1990,8 +1990,8 @@ weston_output_disable(struct weston_output *output);
void
weston_pending_output_coldplug(struct weston_compositor *compositor);
-struct weston_output *
-weston_output_from_resource(struct wl_resource *resource);
+struct weston_head *
+weston_head_from_resource(struct wl_resource *resource);
#ifdef __cplusplus
}
diff --git a/tests/weston-test.c b/tests/weston-test.c
index 6e7beeb7..8bbc2089 100644
--- a/tests/weston-test.c
+++ b/tests/weston-test.c
@@ -484,7 +484,7 @@ capture_screenshot(struct wl_client *client,
struct wl_resource *buffer_resource)
{
struct weston_output *output =
- weston_output_from_resource(output_resource);
+ weston_head_from_resource(output_resource)->output;
struct weston_buffer *buffer =
weston_buffer_from_resource(buffer_resource);
Pekka Paalanen
2017-12-14 11:40:42 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Split out a new function. This is a pure refactoring, no change in
behaviour.

This helps a following patch that adds a loop over output->head_list.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
Reviewed-by: Quentin Glidic <sardemff7+***@sardemff7.net>
---
libweston/compositor.c | 57 +++++++++++++++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 66453ea3..c668aa28 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -76,15 +76,45 @@ weston_output_transform_scale_init(struct weston_output *output,
static void
weston_compositor_build_view_list(struct weston_compositor *compositor);

-static void weston_mode_switch_finish(struct weston_output *output,
- int mode_changed,
- int scale_changed)
+/** Send wl_output events for mode and scale changes
+ *
+ * \param head Send on all resources bound to this head.
+ * \param mode_changed If true, send the current mode.
+ * \param scale_changed If true, send the current scale.
+ */
+static void
+weston_mode_switch_send_events(struct weston_head *head,
+ bool mode_changed, bool scale_changed)
+{
+ struct weston_output *output = head->output;
+ struct wl_resource *resource;
+ int version;
+
+ wl_resource_for_each(resource, &head->resource_list) {
+ if (mode_changed) {
+ wl_output_send_mode(resource,
+ output->current_mode->flags,
+ output->current_mode->width,
+ output->current_mode->height,
+ output->current_mode->refresh);
+ }
+
+ version = wl_resource_get_version(resource);
+ if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
+ wl_output_send_scale(resource, output->current_scale);
+
+ if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+ }
+}
+
+static void
+weston_mode_switch_finish(struct weston_output *output,
+ int mode_changed, int scale_changed)
{
struct weston_seat *seat;
struct weston_head *head;
- struct wl_resource *resource;
pixman_region32_t old_output_region;
- int version;

pixman_region32_init(&old_output_region);
pixman_region32_copy(&old_output_region, &output->region);
@@ -133,22 +163,7 @@ static void weston_mode_switch_finish(struct weston_output *output,
head = &output->head;

/* notify clients of the changes */
- wl_resource_for_each(resource, &head->resource_list) {
- if (mode_changed) {
- wl_output_send_mode(resource,
- output->current_mode->flags,
- output->current_mode->width,
- output->current_mode->height,
- output->current_mode->refresh);
- }
-
- version = wl_resource_get_version(resource);
- if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
- wl_output_send_scale(resource, output->current_scale);
-
- if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
- }
+ weston_mode_switch_send_events(head, mode_changed, scale_changed);
}
--
2.13.6
Derek Foreman
2018-02-01 20:14:35 UTC
Permalink
Post by Pekka Paalanen
Split out a new function. This is a pure refactoring, no change in
behaviour.
This helps a following patch that adds a loop over output->head_list.
Reviewed-by: Derek Foreman <***@osg.samsung.com>

Was hoping we could land this right off, but it depends on weston_head.

Not sure we'll be able to land any of this before we can land all of
this. :)

Thanks,
Derek
Post by Pekka Paalanen
---
libweston/compositor.c | 57 +++++++++++++++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 21 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 66453ea3..c668aa28 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -76,15 +76,45 @@ weston_output_transform_scale_init(struct weston_output *output,
static void
weston_compositor_build_view_list(struct weston_compositor *compositor);
-static void weston_mode_switch_finish(struct weston_output *output,
- int mode_changed,
- int scale_changed)
+/** Send wl_output events for mode and scale changes
+ *
+ * \param head Send on all resources bound to this head.
+ * \param mode_changed If true, send the current mode.
+ * \param scale_changed If true, send the current scale.
+ */
+static void
+weston_mode_switch_send_events(struct weston_head *head,
+ bool mode_changed, bool scale_changed)
+{
+ struct weston_output *output = head->output;
+ struct wl_resource *resource;
+ int version;
+
+ wl_resource_for_each(resource, &head->resource_list) {
+ if (mode_changed) {
+ wl_output_send_mode(resource,
+ output->current_mode->flags,
+ output->current_mode->width,
+ output->current_mode->height,
+ output->current_mode->refresh);
+ }
+
+ version = wl_resource_get_version(resource);
+ if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
+ wl_output_send_scale(resource, output->current_scale);
+
+ if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+ }
+}
+
+static void
+weston_mode_switch_finish(struct weston_output *output,
+ int mode_changed, int scale_changed)
{
struct weston_seat *seat;
struct weston_head *head;
- struct wl_resource *resource;
pixman_region32_t old_output_region;
- int version;
pixman_region32_init(&old_output_region);
pixman_region32_copy(&old_output_region, &output->region);
@@ -133,22 +163,7 @@ static void weston_mode_switch_finish(struct weston_output *output,
head = &output->head;
/* notify clients of the changes */
- wl_resource_for_each(resource, &head->resource_list) {
- if (mode_changed) {
- wl_output_send_mode(resource,
- output->current_mode->flags,
- output->current_mode->width,
- output->current_mode->height,
- output->current_mode->refresh);
- }
-
- version = wl_resource_get_version(resource);
- if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
- wl_output_send_scale(resource, output->current_scale);
-
- if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
- }
+ weston_mode_switch_send_events(head, mode_changed, scale_changed);
}
Pekka Paalanen
2018-02-02 08:15:50 UTC
Permalink
On Thu, 1 Feb 2018 14:14:35 -0600
Post by Derek Foreman
Post by Pekka Paalanen
Split out a new function. This is a pure refactoring, no change in
behaviour.
This helps a following patch that adds a loop over output->head_list.
Was hoping we could land this right off, but it depends on weston_head.
Not sure we'll be able to land any of this before we can land all of
this. :)
Indeed, I have already split out, submitted and landed everything I
reasonably could without depending on weston_head or causing excessive
churn, I believe.


Thanks,
pq
Pekka Paalanen
2017-12-14 11:40:43 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The intention is that in the future backends will dynamically allocate
weston_heads based on the resources they have. The lifetime of a
weston_head will be independent of the lifetime of a weston_output it
may be attached to. Backends allocate objects derived from weston_head,
like they currently do for weston_output. Backend will choose when to
destroy a weston_head.

For clone mode, struct weston_output gains head_list member, which is
the list of attached heads that will all show the same framebuffer.
Since heads are growing out of weston_output, management functions are
added.

Detaching a head from an enabled output is allowed to accommodate
disappearing heads. Attaching a head to an enabled output is disallowed
because it may need hardware reconfiguration and testing, and so
requires a weston_output_enable() call.

As a temporary measure, we have one weston_head embedded in
weston_output, so that backends can be migrated individually to the new
allocation scheme.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 216 +++++++++++++++++++++++++++++++++++++++----------
libweston/compositor.h | 7 +-
2 files changed, 181 insertions(+), 42 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index c668aa28..efa961dc 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -160,13 +160,12 @@ weston_mode_switch_finish(struct weston_output *output,
if (!mode_changed && !scale_changed)
return;

- head = &output->head;
-
/* notify clients of the changes */
- weston_mode_switch_send_events(head, mode_changed, scale_changed);
+ wl_list_for_each(head, &output->head_list, output_link)
+ weston_mode_switch_send_events(head,
+ mode_changed, scale_changed);
}

-
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
struct weston_output *resized_output, int delta_width);
@@ -362,12 +361,13 @@ weston_presentation_feedback_present(
struct wl_resource *o;
uint64_t secs;

- head = &output->head;
- wl_resource_for_each(o, &head->resource_list) {
- if (wl_resource_get_client(o) != client)
- continue;
+ wl_list_for_each(head, &output->head_list, output_link) {
+ wl_resource_for_each(o, &head->resource_list) {
+ if (wl_resource_get_client(o) != client)
+ continue;

- wp_presentation_feedback_send_sync_output(feedback->resource, o);
+ wp_presentation_feedback_send_sync_output(feedback->resource, o);
+ }
}

secs = ts->tv_sec;
@@ -988,6 +988,7 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
uint32_t left = es->output_mask & different;
uint32_t output_bit;
struct weston_output *output;
+ struct weston_head *head;

es->output_mask = mask;
if (es->resource == NULL)
@@ -1000,9 +1001,11 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
if (!(output_bit & different))
continue;

- weston_surface_send_enter_leave(es, &output->head,
- output_bit & entered,
- output_bit & left);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ weston_surface_send_enter_leave(es, head,
+ output_bit & entered,
+ output_bit & left);
+ }
}
}

@@ -4386,6 +4389,98 @@ weston_head_from_resource(struct wl_resource *resource)
return wl_resource_get_user_data(resource);
}

+/** Initialize a pre-allocated weston_head
+ *
+ * \param head The head to initialize.
+ *
+ * The head will be safe to attach, detach and release.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+static void
+weston_head_init(struct weston_head *head)
+{
+ /* Add some (in)sane defaults which can be used
+ * for checking if an output was properly configured
+ */
+ memset(head, 0, sizeof *head);
+
+ wl_list_init(&head->output_link);
+ wl_list_init(&head->resource_list);
+}
+
+/** Attach a head to an inactive output
+ *
+ * \param output The output to attach to.
+ * \param head The head that is not yet attached.
+ * \return 0 on success, -1 on failure.
+ *
+ * Attaches the given head to the output. All heads of an output are clones
+ * and share the resolution and timings.
+ *
+ * Cloning heads this way uses less resources than creating an output for
+ * each head, but is not always possible due to environment, driver and hardware
+ * limitations.
+ *
+ * On failure, the head remains unattached. Success of this function does not
+ * guarantee the output configuration is actually valid. The final checks are
+ * made on weston_output_enable().
+ *
+ * \memberof weston_output
+ */
+static int
+weston_output_attach_head(struct weston_output *output,
+ struct weston_head *head)
+{
+ if (output->enabled)
+ return -1;
+
+ if (!wl_list_empty(&head->output_link))
+ return -1;
+
+ /* XXX: no support for multi-head yet */
+ if (!wl_list_empty(&output->head_list))
+ return -1;
+
+ head->output = output;
+ wl_list_insert(output->head_list.prev, &head->output_link);
+
+ return 0;
+}
+
+/** Detach a head from its output
+ *
+ * \param head The head to detach.
+ *
+ * It is safe to detach a non-attached head.
+ *
+ * \memberof weston_head
+ */
+static void
+weston_head_detach(struct weston_head *head)
+{
+ wl_list_remove(&head->output_link);
+ wl_list_init(&head->output_link);
+ head->output = NULL;
+}
+
+/** Destroy a head
+ *
+ * \param head The head to be released.
+ *
+ * Destroys the head. The caller is responsible for freeing the memory pointed
+ * to by \c head.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+static void
+weston_head_release(struct weston_head *head)
+{
+ weston_head_detach(head);
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4581,8 +4676,9 @@ weston_output_init_geometry(struct weston_output *output, int x, int y)
WL_EXPORT void
weston_output_move(struct weston_output *output, int x, int y)
{
- struct weston_head *head = &output->head;
+ struct weston_head *head;
struct wl_resource *resource;
+ int ver;

output->move_x = x - output->x;
output->move_y = y - output->y;
@@ -4598,19 +4694,22 @@ weston_output_move(struct weston_output *output, int x, int y)
wl_signal_emit(&output->compositor->output_moved_signal, output);

/* Notify clients of the change for output position. */
- wl_resource_for_each(resource, &head->resource_list) {
- wl_output_send_geometry(resource,
- output->x,
- output->y,
- head->mm_width,
- head->mm_height,
- head->subpixel,
- head->make,
- head->model,
- output->transform);
-
- if (wl_resource_get_version(resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ wl_resource_for_each(resource, &head->resource_list) {
+ wl_output_send_geometry(resource,
+ output->x,
+ output->y,
+ head->mm_width,
+ head->mm_height,
+ head->subpixel,
+ head->make,
+ head->model,
+ output->transform);
+
+ ver = wl_resource_get_version(resource);
+ if (ver >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+ }
}
}

@@ -4650,11 +4749,11 @@ weston_compositor_add_output(struct weston_compositor *compositor,
wl_list_insert(compositor->output_list.prev, &output->link);
output->enabled = true;

- head = &output->head;
- head->output = output;
- head->global = wl_global_create(compositor->wl_display,
- &wl_output_interface, 3,
- head, bind_output);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ head->global = wl_global_create(compositor->wl_display,
+ &wl_output_interface, 3,
+ head, bind_output);
+ }

wl_signal_emit(&compositor->output_created_signal, output);

@@ -4747,11 +4846,12 @@ weston_compositor_remove_output(struct weston_output *output)
wl_signal_emit(&compositor->output_destroyed_signal, output);
wl_signal_emit(&output->destroy_signal, output);

- head = &output->head;
- wl_global_destroy(head->global);
- head->global = NULL;
- wl_resource_for_each(resource, &head->resource_list) {
- wl_resource_set_destructor(resource, NULL);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ wl_global_destroy(head->global);
+ head->global = NULL;
+
+ wl_resource_for_each(resource, &head->resource_list)
+ wl_resource_set_destructor(resource, NULL);
}

compositor->output_id_pool &= ~(1u << output->id);
@@ -4829,19 +4929,19 @@ weston_output_init(struct weston_output *output,
struct weston_compositor *compositor,
const char *name)
{
- struct weston_head *head = &output->head;
-
output->compositor = compositor;
output->destroying = 0;
output->name = strdup(name);
wl_list_init(&output->link);
output->enabled = false;

+ wl_list_init(&output->head_list);
+
+ weston_head_init(&output->head);
+
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
*/
- head->mm_width = 0;
- head->mm_height = 0;
output->scale = 0;
/* Can't use -1 on uint32_t and 0 is valid enum value */
output->transform = UINT32_MAX;
@@ -4915,6 +5015,7 @@ weston_output_enable(struct weston_output *output)
struct weston_compositor *c = output->compositor;
struct weston_output *iterator;
int x = 0, y = 0;
+ int ret;

if (output->enabled) {
weston_log("Error: attempt to enable an enabled output '%s'\n",
@@ -4948,9 +5049,14 @@ weston_output_enable(struct weston_output *output)
wl_signal_init(&output->frame_signal);
wl_signal_init(&output->destroy_signal);
wl_list_init(&output->animation_list);
- wl_list_init(&output->head.resource_list);
wl_list_init(&output->feedback_list);

+ /* XXX: Temporary until all backends are converted. */
+ if (wl_list_empty(&output->head_list)) {
+ ret = weston_output_attach_head(output, &output->head);
+ assert(ret == 0);
+ }
+
/* Enable the output (set up the crtc or create a
* window representing the output, set up the
* renderer, etc)
@@ -5042,6 +5148,8 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)
WL_EXPORT void
weston_output_release(struct weston_output *output)
{
+ struct weston_head *head;
+
output->destroying = 1;

if (output->enabled)
@@ -5050,9 +5158,35 @@ weston_output_release(struct weston_output *output)
pixman_region32_fini(&output->region);
pixman_region32_fini(&output->previous_damage);
wl_list_remove(&output->link);
+
+ while (!wl_list_empty(&output->head_list)) {
+ head = weston_output_get_first_head(output);
+ weston_head_detach(head);
+ }
+
+ weston_head_release(&output->head);
+
free(output->name);
}

+/** When you need a head...
+ *
+ * This function is a hack, used until all code has been converted to become
+ * multi-head aware.
+ *
+ * \param output The weston_output whose head to get.
+ * \return The first head in the output's list.
+ */
+WL_EXPORT struct weston_head *
+weston_output_get_first_head(struct weston_output *output)
+{
+ if (wl_list_empty(&output->head_list))
+ return NULL;
+
+ return container_of(output->head_list.next,
+ struct weston_head, output_link);
+}
+
static void
destroy_viewport(struct wl_resource *resource)
{
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 5e0a2867..456adcc8 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -154,6 +154,7 @@ enum dpms_enum {
*/
struct weston_head {
struct weston_output *output; /**< the output driving this head */
+ struct wl_list output_link; /**< in weston_output::head_list */

struct wl_list resource_list; /**< wl_output protocol objects */
struct wl_global *global; /**< wl_output global */
@@ -226,7 +227,8 @@ struct weston_output {
struct weston_mode *original_mode;
struct wl_list mode_list;

- struct weston_head head;
+ struct weston_head head; /**< head for unconverted backends */
+ struct wl_list head_list; /**< List of driven weston_heads */

void (*start_repaint_loop)(struct weston_output *output);
int (*repaint)(struct weston_output *output,
@@ -1993,6 +1995,9 @@ weston_pending_output_coldplug(struct weston_compositor *compositor);
struct weston_head *
weston_head_from_resource(struct wl_resource *resource);

+struct weston_head *
+weston_output_get_first_head(struct weston_output *output);
+
#ifdef __cplusplus
}
#endif
--
2.13.6
Derek Foreman
2018-02-01 21:36:55 UTC
Permalink
Post by Pekka Paalanen
The intention is that in the future backends will dynamically allocate
weston_heads based on the resources they have. The lifetime of a
weston_head will be independent of the lifetime of a weston_output it
may be attached to. Backends allocate objects derived from weston_head,
like they currently do for weston_output. Backend will choose when to
destroy a weston_head.
For clone mode, struct weston_output gains head_list member, which is
the list of attached heads that will all show the same framebuffer.
Since heads are growing out of weston_output, management functions are
added.
Detaching a head from an enabled output is allowed to accommodate
disappearing heads. Attaching a head to an enabled output is disallowed
because it may need hardware reconfiguration and testing, and so
requires a weston_output_enable() call.
As a temporary measure, we have one weston_head embedded in
weston_output, so that backends can be migrated individually to the new
allocation scheme.
---
libweston/compositor.c | 216 +++++++++++++++++++++++++++++++++++++++----------
libweston/compositor.h | 7 +-
2 files changed, 181 insertions(+), 42 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index c668aa28..efa961dc 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -160,13 +160,12 @@ weston_mode_switch_finish(struct weston_output *output,
if (!mode_changed && !scale_changed)
return;
- head = &output->head;
-
/* notify clients of the changes */
- weston_mode_switch_send_events(head, mode_changed, scale_changed);
+ wl_list_for_each(head, &output->head_list, output_link)
+ weston_mode_switch_send_events(head,
+ mode_changed, scale_changed);
}
-
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
struct weston_output *resized_output, int delta_width);
@@ -362,12 +361,13 @@ weston_presentation_feedback_present(
struct wl_resource *o;
uint64_t secs;
- head = &output->head;
- wl_resource_for_each(o, &head->resource_list) {
- if (wl_resource_get_client(o) != client)
- continue;
+ wl_list_for_each(head, &output->head_list, output_link) {
+ wl_resource_for_each(o, &head->resource_list) {
+ if (wl_resource_get_client(o) != client)
+ continue;
- wp_presentation_feedback_send_sync_output(feedback->resource, o);
+ wp_presentation_feedback_send_sync_output(feedback->resource, o);
+ }
}
secs = ts->tv_sec;
@@ -988,6 +988,7 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
uint32_t left = es->output_mask & different;
uint32_t output_bit;
struct weston_output *output;
+ struct weston_head *head;
es->output_mask = mask;
if (es->resource == NULL)
@@ -1000,9 +1001,11 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
if (!(output_bit & different))
continue;
- weston_surface_send_enter_leave(es, &output->head,
- output_bit & entered,
- output_bit & left);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ weston_surface_send_enter_leave(es, head,
+ output_bit & entered,
+ output_bit & left);
+ }
}
}
@@ -4386,6 +4389,98 @@ weston_head_from_resource(struct wl_resource *resource)
return wl_resource_get_user_data(resource);
}
+/** Initialize a pre-allocated weston_head
+ *
+ * \param head The head to initialize.
+ *
+ * The head will be safe to attach, detach and release.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+static void
+weston_head_init(struct weston_head *head)
+{
+ /* Add some (in)sane defaults which can be used
+ * for checking if an output was properly configured
+ */
+ memset(head, 0, sizeof *head);
+
+ wl_list_init(&head->output_link);
+ wl_list_init(&head->resource_list);
+}
+
+/** Attach a head to an inactive output
+ *
+ * \param output The output to attach to.
+ * \param head The head that is not yet attached.
+ * \return 0 on success, -1 on failure.
+ *
+ * Attaches the given head to the output. All heads of an output are clones
+ * and share the resolution and timings.
+ *
+ * Cloning heads this way uses less resources than creating an output for
+ * each head, but is not always possible due to environment, driver and hardware
+ * limitations.
+ *
+ * On failure, the head remains unattached. Success of this function does not
+ * guarantee the output configuration is actually valid. The final checks are
+ * made on weston_output_enable().
+ *
+ * \memberof weston_output
+ */
+static int
+weston_output_attach_head(struct weston_output *output,
+ struct weston_head *head)
+{
+ if (output->enabled)
+ return -1;
Would it be reasonable to make !output->enabled an assert()?
Post by Pekka Paalanen
+
+ if (!wl_list_empty(&head->output_link))
+ return -1;
+
+ /* XXX: no support for multi-head yet */
+ if (!wl_list_empty(&output->head_list))
+ return -1;
+
+ head->output = output;
+ wl_list_insert(output->head_list.prev, &head->output_link);
+
+ return 0;
+}
+
+/** Detach a head from its output
+ *
+ * \param head The head to detach.
+ *
+ * It is safe to detach a non-attached head.
+ *
+ * \memberof weston_head
+ */
+static void
+weston_head_detach(struct weston_head *head)
+{
+ wl_list_remove(&head->output_link);
+ wl_list_init(&head->output_link);
+ head->output = NULL;
+}
+
+/** Destroy a head
+ *
+ * \param head The head to be released.
+ *
+ * Destroys the head. The caller is responsible for freeing the memory pointed
+ * to by \c head.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+static void
+weston_head_release(struct weston_head *head)
+{
+ weston_head_detach(head);
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4581,8 +4676,9 @@ weston_output_init_geometry(struct weston_output *output, int x, int y)
WL_EXPORT void
weston_output_move(struct weston_output *output, int x, int y)
{
- struct weston_head *head = &output->head;
+ struct weston_head *head;
struct wl_resource *resource;
+ int ver;
output->move_x = x - output->x;
output->move_y = y - output->y;
@@ -4598,19 +4694,22 @@ weston_output_move(struct weston_output *output, int x, int y)
wl_signal_emit(&output->compositor->output_moved_signal, output);
/* Notify clients of the change for output position. */
- wl_resource_for_each(resource, &head->resource_list) {
- wl_output_send_geometry(resource,
- output->x,
- output->y,
- head->mm_width,
- head->mm_height,
- head->subpixel,
- head->make,
- head->model,
- output->transform);
-
- if (wl_resource_get_version(resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ wl_resource_for_each(resource, &head->resource_list) {
+ wl_output_send_geometry(resource,
+ output->x,
+ output->y,
+ head->mm_width,
+ head->mm_height,
+ head->subpixel,
+ head->make,
+ head->model,
+ output->transform);
+
+ ver = wl_resource_get_version(resource);
+ if (ver >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+ }
}
}
@@ -4650,11 +4749,11 @@ weston_compositor_add_output(struct weston_compositor *compositor,
wl_list_insert(compositor->output_list.prev, &output->link);
output->enabled = true;
- head = &output->head;
- head->output = output;
- head->global = wl_global_create(compositor->wl_display,
- &wl_output_interface, 3,
- head, bind_output);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ head->global = wl_global_create(compositor->wl_display,
+ &wl_output_interface, 3,
+ head, bind_output);
+ }
wl_signal_emit(&compositor->output_created_signal, output);
@@ -4747,11 +4846,12 @@ weston_compositor_remove_output(struct weston_output *output)
wl_signal_emit(&compositor->output_destroyed_signal, output);
wl_signal_emit(&output->destroy_signal, output);
- head = &output->head;
- wl_global_destroy(head->global);
- head->global = NULL;
- wl_resource_for_each(resource, &head->resource_list) {
- wl_resource_set_destructor(resource, NULL);
+ wl_list_for_each(head, &output->head_list, output_link) {
+ wl_global_destroy(head->global);
+ head->global = NULL;
+
+ wl_resource_for_each(resource, &head->resource_list)
+ wl_resource_set_destructor(resource, NULL);
}
compositor->output_id_pool &= ~(1u << output->id);
@@ -4829,19 +4929,19 @@ weston_output_init(struct weston_output *output,
struct weston_compositor *compositor,
const char *name)
{
- struct weston_head *head = &output->head;
-
output->compositor = compositor;
output->destroying = 0;
output->name = strdup(name);
wl_list_init(&output->link);
output->enabled = false;
+ wl_list_init(&output->head_list);
+
+ weston_head_init(&output->head);
+
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
*/
- head->mm_width = 0;
- head->mm_height = 0;
output->scale = 0;
/* Can't use -1 on uint32_t and 0 is valid enum value */
output->transform = UINT32_MAX;
@@ -4915,6 +5015,7 @@ weston_output_enable(struct weston_output *output)
struct weston_compositor *c = output->compositor;
struct weston_output *iterator;
int x = 0, y = 0;
+ int ret;
if (output->enabled) {
weston_log("Error: attempt to enable an enabled output '%s'\n",
@@ -4948,9 +5049,14 @@ weston_output_enable(struct weston_output *output)
wl_signal_init(&output->frame_signal);
wl_signal_init(&output->destroy_signal);
wl_list_init(&output->animation_list);
- wl_list_init(&output->head.resource_list);
wl_list_init(&output->feedback_list);
+ /* XXX: Temporary until all backends are converted. */
+ if (wl_list_empty(&output->head_list)) {
+ ret = weston_output_attach_head(output, &output->head);
+ assert(ret == 0);
+ }
+
/* Enable the output (set up the crtc or create a
* window representing the output, set up the
* renderer, etc)
@@ -5042,6 +5148,8 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)
WL_EXPORT void
weston_output_release(struct weston_output *output)
{
+ struct weston_head *head;
+
output->destroying = 1;
if (output->enabled)
@@ -5050,9 +5158,35 @@ weston_output_release(struct weston_output *output)
pixman_region32_fini(&output->region);
pixman_region32_fini(&output->previous_damage);
wl_list_remove(&output->link);
+
+ while (!wl_list_empty(&output->head_list)) {
+ head = weston_output_get_first_head(output);
+ weston_head_detach(head);
+ }
I feel like I'm missing something here, but... this function looks
multi-head aware, but depends on the "hacky"
weston_output_get_first_head() function? Should this just be turned
into a regular for_each_safe?

It seems like at the end of the series we're left with 4 callers to
weston_output_get_first_head()... The cms-colord site seems potentially
non-trivial to resolve. What else still needs to be made multi-head
aware before the function can be removed?
Post by Pekka Paalanen
+
+ weston_head_release(&output->head);
+
free(output->name);
}
+/** When you need a head...
+ *
+ * This function is a hack, used until all code has been converted to become
+ * multi-head aware.
+ *
+ * \param output The weston_output whose head to get.
+ * \return The first head in the output's list.
+ */
+WL_EXPORT struct weston_head *
+weston_output_get_first_head(struct weston_output *output)
+{
+ if (wl_list_empty(&output->head_list))
+ return NULL;
+
+ return container_of(output->head_list.next,
+ struct weston_head, output_link);
+}
+
static void
destroy_viewport(struct wl_resource *resource)
{
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 5e0a2867..456adcc8 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -154,6 +154,7 @@ enum dpms_enum {
*/
struct weston_head {
struct weston_output *output; /**< the output driving this head */
+ struct wl_list output_link; /**< in weston_output::head_list */
struct wl_list resource_list; /**< wl_output protocol objects */
struct wl_global *global; /**< wl_output global */
@@ -226,7 +227,8 @@ struct weston_output {
struct weston_mode *original_mode;
struct wl_list mode_list;
- struct weston_head head;
+ struct weston_head head; /**< head for unconverted backends */
+ struct wl_list head_list; /**< List of driven weston_heads */
void (*start_repaint_loop)(struct weston_output *output);
int (*repaint)(struct weston_output *output,
@@ -1993,6 +1995,9 @@ weston_pending_output_coldplug(struct weston_compositor *compositor);
struct weston_head *
weston_head_from_resource(struct wl_resource *resource);
+struct weston_head *
+weston_output_get_first_head(struct weston_output *output);
+
#ifdef __cplusplus
}
#endif
Pekka Paalanen
2018-02-02 08:32:33 UTC
Permalink
On Thu, 1 Feb 2018 15:36:55 -0600
Post by Derek Foreman
Post by Pekka Paalanen
The intention is that in the future backends will dynamically allocate
weston_heads based on the resources they have. The lifetime of a
weston_head will be independent of the lifetime of a weston_output it
may be attached to. Backends allocate objects derived from weston_head,
like they currently do for weston_output. Backend will choose when to
destroy a weston_head.
For clone mode, struct weston_output gains head_list member, which is
the list of attached heads that will all show the same framebuffer.
Since heads are growing out of weston_output, management functions are
added.
Detaching a head from an enabled output is allowed to accommodate
disappearing heads. Attaching a head to an enabled output is disallowed
because it may need hardware reconfiguration and testing, and so
requires a weston_output_enable() call.
As a temporary measure, we have one weston_head embedded in
weston_output, so that backends can be migrated individually to the new
allocation scheme.
---
libweston/compositor.c | 216 +++++++++++++++++++++++++++++++++++++++----------
libweston/compositor.h | 7 +-
2 files changed, 181 insertions(+), 42 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index c668aa28..efa961dc 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4386,6 +4389,98 @@ weston_head_from_resource(struct wl_resource *resource)
return wl_resource_get_user_data(resource);
}
+/** Initialize a pre-allocated weston_head
+ *
+ * \param head The head to initialize.
+ *
+ * The head will be safe to attach, detach and release.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+static void
+weston_head_init(struct weston_head *head)
+{
+ /* Add some (in)sane defaults which can be used
+ * for checking if an output was properly configured
+ */
+ memset(head, 0, sizeof *head);
+
+ wl_list_init(&head->output_link);
+ wl_list_init(&head->resource_list);
+}
+
+/** Attach a head to an inactive output
+ *
+ * \param output The output to attach to.
+ * \param head The head that is not yet attached.
+ * \return 0 on success, -1 on failure.
+ *
+ * Attaches the given head to the output. All heads of an output are clones
+ * and share the resolution and timings.
+ *
+ * Cloning heads this way uses less resources than creating an output for
+ * each head, but is not always possible due to environment, driver and hardware
+ * limitations.
+ *
+ * On failure, the head remains unattached. Success of this function does not
+ * guarantee the output configuration is actually valid. The final checks are
+ * made on weston_output_enable().
+ *
+ * \memberof weston_output
+ */
+static int
+weston_output_attach_head(struct weston_output *output,
+ struct weston_head *head)
+{
+ if (output->enabled)
+ return -1;
Would it be reasonable to make !output->enabled an assert()?
I forget if I actually had it like that, but then this function later
becomes public API and I preferred to return an error rather than BOOM.

It happens in "libweston: new head-based output management API".

Is that ok?
Post by Derek Foreman
Post by Pekka Paalanen
+
+ if (!wl_list_empty(&head->output_link))
+ return -1;
+
+ /* XXX: no support for multi-head yet */
+ if (!wl_list_empty(&output->head_list))
+ return -1;
+
+ head->output = output;
+ wl_list_insert(output->head_list.prev, &head->output_link);
+
+ return 0;
+}
@@ -5042,6 +5148,8 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)
WL_EXPORT void
weston_output_release(struct weston_output *output)
{
+ struct weston_head *head;
+
output->destroying = 1;
if (output->enabled)
@@ -5050,9 +5158,35 @@ weston_output_release(struct weston_output *output)
pixman_region32_fini(&output->region);
pixman_region32_fini(&output->previous_damage);
wl_list_remove(&output->link);
+
+ while (!wl_list_empty(&output->head_list)) {
+ head = weston_output_get_first_head(output);
+ weston_head_detach(head);
+ }
I feel like I'm missing something here, but... this function looks
multi-head aware, but depends on the "hacky"
weston_output_get_first_head() function? Should this just be turned
into a regular for_each_safe?
It seems like at the end of the series we're left with 4 callers to
weston_output_get_first_head()... The cms-colord site seems potentially
non-trivial to resolve. What else still needs to be made multi-head
aware before the function can be removed?
Yes, that's a good point. This site is actually the only legit use case
for weston_output_get_first_head(). I agree it is somewhat confusing
and is a result of lazyness. Will fix.

Aside from cms-colord.c, in the full clonemode series (this is a
partial one), the only other use of weston_output_get_first_head() is
in compositor-drm.c drm_output_set_mode().

In drm_output_set_mode(), the first head is used to get the "inherited
mode", that is, the video mode on the output before weston took over.
This video mode is fed into drm_output_choose_initial_mode(). The
trouble is, if we are enabling for the first time an output with
multiple heads, which head's original mode should be used? I suppose
you'd filter out any heads that didn't have any mode set, but how to
pick then? So I went for the easy way out for now.
Very much appreciated.


Thanks,
pq
Derek Foreman
2018-02-02 18:35:42 UTC
Permalink
Post by Pekka Paalanen
On Thu, 1 Feb 2018 15:36:55 -0600
Post by Derek Foreman
Post by Pekka Paalanen
The intention is that in the future backends will dynamically allocate
weston_heads based on the resources they have. The lifetime of a
weston_head will be independent of the lifetime of a weston_output it
may be attached to. Backends allocate objects derived from weston_head,
like they currently do for weston_output. Backend will choose when to
destroy a weston_head.
For clone mode, struct weston_output gains head_list member, which is
the list of attached heads that will all show the same framebuffer.
Since heads are growing out of weston_output, management functions are
added.
Detaching a head from an enabled output is allowed to accommodate
disappearing heads. Attaching a head to an enabled output is disallowed
because it may need hardware reconfiguration and testing, and so
requires a weston_output_enable() call.
As a temporary measure, we have one weston_head embedded in
weston_output, so that backends can be migrated individually to the new
allocation scheme.
---
libweston/compositor.c | 216 +++++++++++++++++++++++++++++++++++++++----------
libweston/compositor.h | 7 +-
2 files changed, 181 insertions(+), 42 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index c668aa28..efa961dc 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4386,6 +4389,98 @@ weston_head_from_resource(struct wl_resource *resource)
return wl_resource_get_user_data(resource);
}
+/** Initialize a pre-allocated weston_head
+ *
+ * \param head The head to initialize.
+ *
+ * The head will be safe to attach, detach and release.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+static void
+weston_head_init(struct weston_head *head)
+{
+ /* Add some (in)sane defaults which can be used
+ * for checking if an output was properly configured
+ */
+ memset(head, 0, sizeof *head);
+
+ wl_list_init(&head->output_link);
+ wl_list_init(&head->resource_list);
+}
+
+/** Attach a head to an inactive output
+ *
+ * \param output The output to attach to.
+ * \param head The head that is not yet attached.
+ * \return 0 on success, -1 on failure.
+ *
+ * Attaches the given head to the output. All heads of an output are clones
+ * and share the resolution and timings.
+ *
+ * Cloning heads this way uses less resources than creating an output for
+ * each head, but is not always possible due to environment, driver and hardware
+ * limitations.
+ *
+ * On failure, the head remains unattached. Success of this function does not
+ * guarantee the output configuration is actually valid. The final checks are
+ * made on weston_output_enable().
+ *
+ * \memberof weston_output
+ */
+static int
+weston_output_attach_head(struct weston_output *output,
+ struct weston_head *head)
+{
+ if (output->enabled)
+ return -1;
Would it be reasonable to make !output->enabled an assert()?
I forget if I actually had it like that, but then this function later
becomes public API and I preferred to return an error rather than BOOM.
It happens in "libweston: new head-based output management API".
Is that ok?
Seems reasonable to me.
Post by Pekka Paalanen
Post by Derek Foreman
Post by Pekka Paalanen
+
+ if (!wl_list_empty(&head->output_link))
+ return -1;
+
+ /* XXX: no support for multi-head yet */
+ if (!wl_list_empty(&output->head_list))
+ return -1;
+
+ head->output = output;
+ wl_list_insert(output->head_list.prev, &head->output_link);
+
+ return 0;
+}
@@ -5042,6 +5148,8 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)
WL_EXPORT void
weston_output_release(struct weston_output *output)
{
+ struct weston_head *head;
+
output->destroying = 1;
if (output->enabled)
@@ -5050,9 +5158,35 @@ weston_output_release(struct weston_output *output)
pixman_region32_fini(&output->region);
pixman_region32_fini(&output->previous_damage);
wl_list_remove(&output->link);
+
+ while (!wl_list_empty(&output->head_list)) {
+ head = weston_output_get_first_head(output);
+ weston_head_detach(head);
+ }
I feel like I'm missing something here, but... this function looks
multi-head aware, but depends on the "hacky"
weston_output_get_first_head() function? Should this just be turned
into a regular for_each_safe?
It seems like at the end of the series we're left with 4 callers to
weston_output_get_first_head()... The cms-colord site seems potentially
non-trivial to resolve. What else still needs to be made multi-head
aware before the function can be removed?
Yes, that's a good point. This site is actually the only legit use case
for weston_output_get_first_head(). I agree it is somewhat confusing
and is a result of lazyness. Will fix.
Aside from cms-colord.c, in the full clonemode series (this is a
partial one), the only other use of weston_output_get_first_head() is
in compositor-drm.c drm_output_set_mode().
In drm_output_set_mode(), the first head is used to get the "inherited
mode", that is, the video mode on the output before weston took over.
This video mode is fed into drm_output_choose_initial_mode(). The
trouble is, if we are enabling for the first time an output with
multiple heads, which head's original mode should be used? I suppose
you'd filter out any heads that didn't have any mode set, but how to
pick then? So I went for the easy way out for now.
(Is it just me or has drm_output_choose_initial_mode's doxy gotten a
little stale?)

Not sure I'm ready to think that far ahead yet, but I'll try...

At some point I think it should be possible for all the heads in an
output to have completely different resolutions as well as refresh
rates. On a windows PC here I can set my 4k monitor and 1920x1200
monitor to be clones, and I can set the resolution to 3840x2160.

This looks poor with black bars top and bottom on the 1920x1200 monitor,
but it does work, and I can see some people thinking this is a
reasonable way to hook up a projector.

So, I think the weston_output resolution and weston_head resolution
should be potentially independent (even when not cloned) of any
modelines available from the head.

That makes picking a potential output mode from several heads a serious
mess. :)

So, drifting slowly back to some kind of point...

Will simply picking the current mode on the first head be surprising to
users? Will it result in situations where switching two monitor cables
will cause surprising behaviour?

I don't really know how we should define surprising in this context, but
I suppose we should do our best to light up all the displays set as
cloned, and also not exit.

I think we should be as lazy as possible in order to get that. I
suspect that level of lazy is somewhere between "just use the first
head" and "savage stack of broken heuristics that fall apart in the dark
corners".

The concept of the mode currently in use for an "output" at weston
startup seems somewhat meaningless when an output is actually an
internal canvas abstraction and all the heads in that output may have
different modes at launch time. :/

(All that said, I'm not sure any of that actually matters in the context
of the series currently under review.)

Thanks,
Derek
Post by Pekka Paalanen
Very much appreciated.
Thanks,
pq
Pekka Paalanen
2018-02-05 10:02:14 UTC
Permalink
On Fri, 2 Feb 2018 12:35:42 -0600
Post by Derek Foreman
Post by Pekka Paalanen
On Thu, 1 Feb 2018 15:36:55 -0600
Post by Pekka Paalanen
The intention is that in the future backends will dynamically allocate
weston_heads based on the resources they have. The lifetime of a
weston_head will be independent of the lifetime of a weston_output it
may be attached to. Backends allocate objects derived from weston_head,
like they currently do for weston_output. Backend will choose when to
destroy a weston_head.
For clone mode, struct weston_output gains head_list member, which is
the list of attached heads that will all show the same framebuffer.
Since heads are growing out of weston_output, management functions are
added.
Detaching a head from an enabled output is allowed to accommodate
disappearing heads. Attaching a head to an enabled output is disallowed
because it may need hardware reconfiguration and testing, and so
requires a weston_output_enable() call.
As a temporary measure, we have one weston_head embedded in
weston_output, so that backends can be migrated individually to the new
allocation scheme.
---
libweston/compositor.c | 216 +++++++++++++++++++++++++++++++++++++++----------
libweston/compositor.h | 7 +-
2 files changed, 181 insertions(+), 42 deletions(-)
Aside from cms-colord.c, in the full clonemode series (this is a
partial one), the only other use of weston_output_get_first_head() is
in compositor-drm.c drm_output_set_mode().
In drm_output_set_mode(), the first head is used to get the "inherited
mode", that is, the video mode on the output before weston took over.
This video mode is fed into drm_output_choose_initial_mode(). The
trouble is, if we are enabling for the first time an output with
multiple heads, which head's original mode should be used? I suppose
you'd filter out any heads that didn't have any mode set, but how to
pick then? So I went for the easy way out for now.
(Is it just me or has drm_output_choose_initial_mode's doxy gotten a
little stale?)
Yes, I thought I fixed it... oh, it's just buried in an unrelated patch
I have never published, so the fix doesn't exist yet.
Post by Derek Foreman
Not sure I'm ready to think that far ahead yet, but I'll try...
At some point I think it should be possible for all the heads in an
output to have completely different resolutions as well as refresh
rates. On a windows PC here I can set my 4k monitor and 1920x1200
monitor to be clones, and I can set the resolution to 3840x2160.
That is not shared-CRTC cloning, that is independent-CRTC cloning. My
work does not include independent-CRTC cloning. This patch series is
purely for shared-CRTC cloning, which implies that all connectors are
driven from the same CRTC, or if multiple CRTCs are used they must be
guaranteed to be gen-locked (for which there is no kernel UABI yet).

The rule I installed here is: if heads might have different timings,
they cannot be attached to the same weston_output. Weston_output
maintains the repaint state machine which must be locked to the
hardware scanout cycle. Therefore there can only be one scanout cycle
timing-wise per weston_output.

Once we fix libweston's damage tracking to cope with overlapping
weston_outputs (I have no immediate plans to work on that, tough), then
the scenario you mentioned can be realized by creating a weston_output
for each monitor and configuring these weston_outputs to display the
same area of the desktop. That way the monitors can have independent
timings, resolution, scaling, rotation, color profile, everything, but
at the cost of having to render framebuffers for each separately.
Post by Derek Foreman
This looks poor with black bars top and bottom on the 1920x1200 monitor,
but it does work, and I can see some people thinking this is a
reasonable way to hook up a projector.
So, I think the weston_output resolution and weston_head resolution
should be potentially independent (even when not cloned) of any
modelines available from the head.
No, I'd like to think my design is much simpler than that. ;-)
Post by Derek Foreman
That makes picking a potential output mode from several heads a serious
mess. :)
So, drifting slowly back to some kind of point...
Will simply picking the current mode on the first head be surprising to
users? Will it result in situations where switching two monitor cables
will cause surprising behaviour?
It's possible that may change the end result, and probably there are
many more causes as well.

I'd like to point out that clone mode is a new feature, so there won't
be a regression at least.
Post by Derek Foreman
I don't really know how we should define surprising in this context, but
I suppose we should do our best to light up all the displays set as
cloned, and also not exit.
I agree.
Post by Derek Foreman
I think we should be as lazy as possible in order to get that. I
suspect that level of lazy is somewhere between "just use the first
head" and "savage stack of broken heuristics that fall apart in the dark
corners".
The concept of the mode currently in use for an "output" at weston
startup seems somewhat meaningless when an output is actually an
internal canvas abstraction and all the heads in that output may have
different modes at launch time. :/
Indeed.
Post by Derek Foreman
(All that said, I'm not sure any of that actually matters in the context
of the series currently under review.)
Yeah, I'd like to think of it as another problem to be solved later.

To be independent of ordering, we would probably want to collect
current modes from all heads and sort that list, then pick...
- largest? high chance of not working for some heads
- smallest? ..maybe

Or repeatedly pick the next largest and check against monitor mode
lists to see if it's there, or against monitor specs to see if it might
work.

Of course, this whole question is avoided if weston.ini contains an
explicit mode to set.

Unless that mode is "vague", and one needs to look for a match from a
list of supported modes. But for that there is something fun like
https://gitlab.collabora.com/pq/weston/commit/060887a14d7955f70d7c39f2dfc358a7a3841633


Thanks,
pq
Post by Derek Foreman
Thanks,
Derek
Post by Pekka Paalanen
Very much appreciated.
Thanks,
pq
Pekka Paalanen
2017-12-14 11:40:44 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Remove the wl_resource in the head's resource list when we are
removing the wl_output global. We sent global removal events to clients,
the resources should become dummies until clients reap them. Reset user
data so that clients triying to use dummy objects don't hit e.g. a freed
head pointer.

This fixes a theoretical issue: if an enabled output is disabled and
then gets enabled again, mode changes and wl_surface.enter/leave would
still attempt to use the dummy objects. If a client destroyed a dummy
object, we don't have the destructor to remove it from the resource
list, and libweston would hit freed memory.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 32 ++++++++++++++++++++++++--------
1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index efa961dc..d314f2d5 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4374,6 +4374,28 @@ bind_output(struct wl_client *client,
wl_output_send_done(resource);
}

+/** Remove the global wl_output protocol object
+ *
+ * \param head The head whose global to remove.
+ *
+ * Also orphans the wl_resources for this head (wl_output).
+ */
+static void
+weston_head_remove_global(struct weston_head *head)
+{
+ struct wl_resource *resource, *tmp;
+
+ if (head->global)
+ wl_global_destroy(head->global);
+ head->global = NULL;
+
+ wl_resource_for_each_safe(resource, tmp, &head->resource_list) {
+ unbind_resource(resource);
+ wl_resource_set_destructor(resource, NULL);
+ wl_resource_set_user_data(resource, NULL);
+ }
+}
+
/** Get the backing object of wl_output
*
* \param resource A wl_output protocol object.
@@ -4823,7 +4845,6 @@ static void
weston_compositor_remove_output(struct weston_output *output)
{
struct weston_compositor *compositor = output->compositor;
- struct wl_resource *resource;
struct weston_view *view;
struct weston_head *head;

@@ -4846,13 +4867,8 @@ weston_compositor_remove_output(struct weston_output *output)
wl_signal_emit(&compositor->output_destroyed_signal, output);
wl_signal_emit(&output->destroy_signal, output);

- wl_list_for_each(head, &output->head_list, output_link) {
- wl_global_destroy(head->global);
- head->global = NULL;
-
- wl_resource_for_each(resource, &head->resource_list)
- wl_resource_set_destructor(resource, NULL);
- }
+ wl_list_for_each(head, &output->head_list, output_link)
+ weston_head_remove_global(head);

compositor->output_id_pool &= ~(1u << output->id);
output->id = 0xffffffff; /* invalid */
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:45 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Duplicate these strings to decouple their lifetime from whatever the
backends used. This should prevent hard to catch use after frees and
such problems in the future.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
Reviewed-by: Quentin Glidic <sardemff7+***@sardemff7.net>
---
libweston/compositor.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index d314f2d5..1e62254a 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4501,6 +4501,10 @@ static void
weston_head_release(struct weston_head *head)
{
weston_head_detach(head);
+
+ free(head->make);
+ free(head->model);
+ free(head->serial_number);
}

/** Store monitor make, model and serial number
@@ -4522,9 +4526,13 @@ weston_head_set_monitor_strings(struct weston_head *head,
const char *model,
const char *serialno)
{
- head->make = (char *)make;
- head->model = (char *)model;
- head->serial_number = (char *)serialno;
+ free(head->make);
+ free(head->model);
+ free(head->serial_number);
+
+ head->make = make ? strdup(make) : NULL;
+ head->model = model ? strdup(model) : NULL;
+ head->serial_number = serialno ? strdup(serialno) : NULL;
}

/** Store physical image size
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:47 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Heads need to be named, so they can be referenced in logs and
configuration sources.

When clone mode is implemented, output and head names may differ.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 10 ++++++++--
libweston/compositor.h | 2 ++
2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 1e62254a..b0d1629e 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4414,14 +4414,18 @@ weston_head_from_resource(struct wl_resource *resource)
/** Initialize a pre-allocated weston_head
*
* \param head The head to initialize.
+ * \param name The head name, e.g. the connector name or equivalent.
*
* The head will be safe to attach, detach and release.
*
+ * The name is used in logs, and can be used by compositors as a configuration
+ * identifier.
+ *
* \memberof weston_head
* \internal
*/
static void
-weston_head_init(struct weston_head *head)
+weston_head_init(struct weston_head *head, const char *name)
{
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
@@ -4430,6 +4434,7 @@ weston_head_init(struct weston_head *head)

wl_list_init(&head->output_link);
wl_list_init(&head->resource_list);
+ head->name = strdup(name);
}

/** Attach a head to an inactive output
@@ -4505,6 +4510,7 @@ weston_head_release(struct weston_head *head)
free(head->make);
free(head->model);
free(head->serial_number);
+ free(head->name);
}

/** Store monitor make, model and serial number
@@ -4961,7 +4967,7 @@ weston_output_init(struct weston_output *output,

wl_list_init(&output->head_list);

- weston_head_init(&output->head);
+ weston_head_init(&output->head, name);

/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 456adcc8..02b5fa32 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -166,6 +166,8 @@ struct weston_head {
char *serial_number; /**< monitor serial */
uint32_t subpixel; /**< enum wl_output_subpixel */
bool connection_internal; /**< embedded monitor (e.g. laptop) */
+
+ char *name; /**< head name, e.g. connector name */
};

struct weston_output {
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:46 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The 'head' member of 'struct weston_output' is going to go unused and
then disappear, so stop using it and find a head from the proper list.

However, this leaves a problem in cms-colord: if you have multiple
monitors driver with the same CRTC, what do you say to the color
management system? The monitors could be different, but all the color
LUTs etc. are in the CRTC and are shared, as is the framebuffer.

Do the simple hack here and just use whatever head happens to be the
first in the list.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/cms-colord.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/compositor/cms-colord.c b/compositor/cms-colord.c
index f421773b..9061091b 100644
--- a/compositor/cms-colord.c
+++ b/compositor/cms-colord.c
@@ -102,10 +102,13 @@ edid_value_valid(const char *str)
static gchar *
get_output_id(struct cms_colord *cms, struct weston_output *o)
{
- struct weston_head *head = &o->head;
+ struct weston_head *head;
const gchar *tmp;
GString *device_id;

+ /* XXX: What to do with multiple heads? */
+ head = weston_output_get_first_head(o);
+
/* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
* for format and allowed values */
device_id = g_string_new("xrandr");
@@ -231,7 +234,7 @@ colord_notifier_output_destroy(struct wl_listener *listener, void *data)
static void
colord_output_created(struct cms_colord *cms, struct weston_output *o)
{
- struct weston_head *head = &o->head;
+ struct weston_head *head;
CdDevice *device;
const gchar *tmp;
gchar *device_id;
@@ -239,6 +242,9 @@ colord_output_created(struct cms_colord *cms, struct weston_output *o)
GHashTable *device_props;
struct cms_output *ocms;

+ /* XXX: What to do with multiple heads? */
+ head = weston_output_get_first_head(o);
+
/* create device */
device_id = get_output_id(cms, o);
weston_log("colord: output added %s\n", device_id);
--
2.13.6
Derek Foreman
2018-02-01 22:00:28 UTC
Permalink
Post by Pekka Paalanen
The 'head' member of 'struct weston_output' is going to go unused and
then disappear, so stop using it and find a head from the proper list.
However, this leaves a problem in cms-colord: if you have multiple
monitors driver with the same CRTC, what do you say to the color
management system? The monitors could be different, but all the color
LUTs etc. are in the CRTC and are shared, as is the framebuffer.
Do the simple hack here and just use whatever head happens to be the
first in the list.
I am a complete non-expert in this area, so if I'm making no sense feel
free to tell me to shut up...

I think if someone's going through the effort to properly setup color
management, then we can't use cloned heads off a single CRTC?

We should probably disallow a CRTCs to drive multiple heads if two or
more of those heads have differing color profiles?

If I went to the trouble of calibrating two displays, I would probably
be extremely surprised to see them looking different with clone mode
enabled.

(All previous patches have my RB, but I'm worried about this one.)

Thanks,
Derek
Post by Pekka Paalanen
---
compositor/cms-colord.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/compositor/cms-colord.c b/compositor/cms-colord.c
index f421773b..9061091b 100644
--- a/compositor/cms-colord.c
+++ b/compositor/cms-colord.c
@@ -102,10 +102,13 @@ edid_value_valid(const char *str)
static gchar *
get_output_id(struct cms_colord *cms, struct weston_output *o)
{
- struct weston_head *head = &o->head;
+ struct weston_head *head;
const gchar *tmp;
GString *device_id;
+ /* XXX: What to do with multiple heads? */
+ head = weston_output_get_first_head(o);
+
/* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
* for format and allowed values */
device_id = g_string_new("xrandr");
@@ -231,7 +234,7 @@ colord_notifier_output_destroy(struct wl_listener *listener, void *data)
static void
colord_output_created(struct cms_colord *cms, struct weston_output *o)
{
- struct weston_head *head = &o->head;
+ struct weston_head *head;
CdDevice *device;
const gchar *tmp;
gchar *device_id;
@@ -239,6 +242,9 @@ colord_output_created(struct cms_colord *cms, struct weston_output *o)
GHashTable *device_props;
struct cms_output *ocms;
+ /* XXX: What to do with multiple heads? */
+ head = weston_output_get_first_head(o);
+
/* create device */
device_id = get_output_id(cms, o);
weston_log("colord: output added %s\n", device_id);
Daniel Stone
2018-02-01 22:10:33 UTC
Permalink
Hi,
Post by Pekka Paalanen
The 'head' member of 'struct weston_output' is going to go unused and
then disappear, so stop using it and find a head from the proper list.
However, this leaves a problem in cms-colord: if you have multiple
monitors driver with the same CRTC, what do you say to the color
management system? The monitors could be different, but all the color
LUTs etc. are in the CRTC and are shared, as is the framebuffer.
Do the simple hack here and just use whatever head happens to be the
first in the list.
I am a complete non-expert in this area, so if I'm making no sense feel free
to tell me to shut up...
I think if someone's going through the effort to properly setup color
management, then we can't use cloned heads off a single CRTC?
We should probably disallow a CRTCs to drive multiple heads if two or more
of those heads have differing color profiles?
If I went to the trouble of calibrating two displays, I would probably be
extremely surprised to see them looking different with clone mode enabled.
Yeah, what Derek said. I think non-homogeneous colour properties when
colour management is enabled, should be cause to unclone. That being
said, it's a niche enough usecase that I'd be comfortable landing it
with documentation that it was a known shortcoming. I'd be super
comfortable landing it if we also had an output configuration tweak we
could use to force non-clone mode, or really even just a simple
out-of-tree patch (or environment variable, a la atomic?) that allows
people to work around it easily enough by disabling clones.

Cheers,
Daniel
Pekka Paalanen
2018-02-02 09:45:18 UTC
Permalink
On Thu, 1 Feb 2018 22:10:33 +0000
Post by Daniel Stone
Hi,
Post by Pekka Paalanen
The 'head' member of 'struct weston_output' is going to go unused and
then disappear, so stop using it and find a head from the proper list.
However, this leaves a problem in cms-colord: if you have multiple
monitors driver with the same CRTC, what do you say to the color
management system? The monitors could be different, but all the color
LUTs etc. are in the CRTC and are shared, as is the framebuffer.
Do the simple hack here and just use whatever head happens to be the
first in the list.
I am a complete non-expert in this area, so if I'm making no sense feel free
to tell me to shut up...
I think if someone's going through the effort to properly setup color
management, then we can't use cloned heads off a single CRTC?
We should probably disallow a CRTCs to drive multiple heads if two or more
of those heads have differing color profiles?
If I went to the trouble of calibrating two displays, I would probably be
extremely surprised to see them looking different with clone mode enabled.
Yeah, what Derek said. I think non-homogeneous colour properties when
colour management is enabled, should be cause to unclone. That being
said, it's a niche enough usecase that I'd be comfortable landing it
with documentation that it was a known shortcoming. I'd be super
comfortable landing it if we also had an output configuration tweak we
could use to force non-clone mode, or really even just a simple
out-of-tree patch (or environment variable, a la atomic?) that allows
people to work around it easily enough by disabling clones.
Hi,

I agree with Derek's suggestion that we should check color properties
and fail the shared-CRTC cloning, and with Daniel that we could just
document this. I'm not sure what the "output configuration tweak"
refers to. Do you mean an option that would just disable shared-CRTC
cloning?

We have Weston that handles all option parsing, and Weston also does
the decision to use or not use shared-CRTC cloning by how it attaches
heads to outputs. Libweston will not try shared-CRTC cloning unless
explicitly told to, and it will not transparently fall back to
something else. After the complete clone mode series, which will not
include independent-CRTC cloning support, the option to disable
shared-CRTC cloning would be equivalent to a weston.ini that does not
configure cloning at all.

However, the weston.ini syntax as is does not differentiate between
shared-CRTC and independent-CRTC cloning. My intention is that Weston
(not libweston) attemps shared-CRTC cloning and falls back to
independent-CRTC cloning.

For the cms-colord plugin to be able to veto shared-CRTC cloning we
would need libweston infrastructure work to either offer plugins a veto
API or make libweston core aware of color profiles. An alternative
would be to have Weston query the color management plugin directly if
heads are compatible for shared-CRTC mode.

One more idea coming to mind is that the "same-as" directive in
weston.ini could refer to either exclusively shared-CRTC clone mode or
shared-CRTC with fallback to independent-CRTC mode, and we could use a
hypothetical future output layout configuration directives to force
independent-CRTC mode by defining two outputs to show the same area of
the desktop.

Given that, what would you recommend for now?


Thanks,
pq
Derek Foreman
2018-02-02 19:40:49 UTC
Permalink
Post by Pekka Paalanen
On Thu, 1 Feb 2018 22:10:33 +0000
Post by Daniel Stone
Hi,
Post by Pekka Paalanen
The 'head' member of 'struct weston_output' is going to go unused and
then disappear, so stop using it and find a head from the proper list.
However, this leaves a problem in cms-colord: if you have multiple
monitors driver with the same CRTC, what do you say to the color
management system? The monitors could be different, but all the color
LUTs etc. are in the CRTC and are shared, as is the framebuffer.
Do the simple hack here and just use whatever head happens to be the
first in the list.
I am a complete non-expert in this area, so if I'm making no sense feel free
to tell me to shut up...
I think if someone's going through the effort to properly setup color
management, then we can't use cloned heads off a single CRTC?
We should probably disallow a CRTCs to drive multiple heads if two or more
of those heads have differing color profiles?
If I went to the trouble of calibrating two displays, I would probably be
extremely surprised to see them looking different with clone mode enabled.
Yeah, what Derek said. I think non-homogeneous colour properties when
colour management is enabled, should be cause to unclone. That being
said, it's a niche enough usecase that I'd be comfortable landing it
with documentation that it was a known shortcoming. I'd be super
comfortable landing it if we also had an output configuration tweak we
could use to force non-clone mode, or really even just a simple
out-of-tree patch (or environment variable, a la atomic?) that allows
people to work around it easily enough by disabling clones.
Hi,
I agree with Derek's suggestion that we should check color properties
and fail the shared-CRTC cloning, and with Daniel that we could just
document this. I'm not sure what the "output configuration tweak"
refers to. Do you mean an option that would just disable shared-CRTC
cloning?
I can get behind just documenting it.
Post by Pekka Paalanen
We have Weston that handles all option parsing, and Weston also does
the decision to use or not use shared-CRTC cloning by how it attaches
heads to outputs. Libweston will not try shared-CRTC cloning unless
explicitly told to, and it will not transparently fall back to
something else. After the complete clone mode series, which will not
include independent-CRTC cloning support, the option to disable
shared-CRTC cloning would be equivalent to a weston.ini that does not
configure cloning at all.
QED. :)

For myself, I've been struggling with the ramifications of shared-CRTC
vs independent-CRTC cloning, and didn't realize until you pointed out to
me out-of-band that independent-CRTC cloning isn't just fallback
operation (and in fact won't work at all without additional work).
Post by Pekka Paalanen
However, the weston.ini syntax as is does not differentiate between
shared-CRTC and independent-CRTC cloning. My intention is that Weston
(not libweston) attemps shared-CRTC cloning and falls back to
independent-CRTC cloning.
For the cms-colord plugin to be able to veto shared-CRTC cloning we
would need libweston infrastructure work to either offer plugins a veto
API or make libweston core aware of color profiles. An alternative
would be to have Weston query the color management plugin directly if
heads are compatible for shared-CRTC mode.
Yes, it had occurred to me that this could be particularly painful. :(

The plug-in veto idea, seems like it would be a fairly heavy amount of
overkill if cms-colord would forever be the only plug-in to actually use it.

If I'm understanding all the pieces in play at this point... We'd
really just be giving cms-colord the ability to stop weston from
launching at all, since independent-CRTC cloning doesn't actually work,
and is (quite reasonably!) out of scope for this series and its follow up?
Post by Pekka Paalanen
One more idea coming to mind is that the "same-as" directive in
weston.ini could refer to either exclusively shared-CRTC clone mode or
shared-CRTC with fallback to independent-CRTC mode, and we could use a
hypothetical future output layout configuration directives to force
independent-CRTC mode by defining two outputs to show the same area of
the desktop.
Given that, what would you recommend for now?
Can we generate a log message when cms-colord configuration results in
suboptimal display for cloned heads (possibly including some strings to
identify the display/displays that look wrong)?

Or even a warning when cms-colord and clones are both enabled at the
same time?

At this point, I think whoever the first person to need clone+cms-colord
simultaneously is going to have to step up and do some heavy lifting.

As long as we're not silently doing something surprising with color
profiles, I think it should be ok? Since clone mode is all new, this
isn't going to cause a regression for anyone.
Post by Pekka Paalanen
Thanks,
pq
Pekka Paalanen
2018-02-05 10:26:47 UTC
Permalink
On Fri, 2 Feb 2018 13:40:49 -0600
Post by Derek Foreman
Post by Pekka Paalanen
On Thu, 1 Feb 2018 22:10:33 +0000
Post by Daniel Stone
Hi,
Post by Pekka Paalanen
The 'head' member of 'struct weston_output' is going to go unused and
then disappear, so stop using it and find a head from the proper list.
However, this leaves a problem in cms-colord: if you have multiple
monitors driver with the same CRTC, what do you say to the color
management system? The monitors could be different, but all the color
LUTs etc. are in the CRTC and are shared, as is the framebuffer.
Do the simple hack here and just use whatever head happens to be the
first in the list.
I am a complete non-expert in this area, so if I'm making no sense feel free
to tell me to shut up...
I think if someone's going through the effort to properly setup color
management, then we can't use cloned heads off a single CRTC?
We should probably disallow a CRTCs to drive multiple heads if two or more
of those heads have differing color profiles?
If I went to the trouble of calibrating two displays, I would probably be
extremely surprised to see them looking different with clone mode enabled.
Yeah, what Derek said. I think non-homogeneous colour properties when
colour management is enabled, should be cause to unclone. That being
said, it's a niche enough usecase that I'd be comfortable landing it
with documentation that it was a known shortcoming. I'd be super
comfortable landing it if we also had an output configuration tweak we
could use to force non-clone mode, or really even just a simple
out-of-tree patch (or environment variable, a la atomic?) that allows
people to work around it easily enough by disabling clones.
Hi,
I agree with Derek's suggestion that we should check color properties
and fail the shared-CRTC cloning, and with Daniel that we could just
document this. I'm not sure what the "output configuration tweak"
refers to. Do you mean an option that would just disable shared-CRTC
cloning?
I can get behind just documenting it.
Post by Pekka Paalanen
We have Weston that handles all option parsing, and Weston also does
the decision to use or not use shared-CRTC cloning by how it attaches
heads to outputs. Libweston will not try shared-CRTC cloning unless
explicitly told to, and it will not transparently fall back to
something else. After the complete clone mode series, which will not
include independent-CRTC cloning support, the option to disable
shared-CRTC cloning would be equivalent to a weston.ini that does not
configure cloning at all.
QED. :)
For myself, I've been struggling with the ramifications of shared-CRTC
vs independent-CRTC cloning, and didn't realize until you pointed out to
me out-of-band that independent-CRTC cloning isn't just fallback
operation (and in fact won't work at all without additional work).
Post by Pekka Paalanen
However, the weston.ini syntax as is does not differentiate between
shared-CRTC and independent-CRTC cloning. My intention is that Weston
(not libweston) attemps shared-CRTC cloning and falls back to
independent-CRTC cloning.
For the cms-colord plugin to be able to veto shared-CRTC cloning we
would need libweston infrastructure work to either offer plugins a veto
API or make libweston core aware of color profiles. An alternative
would be to have Weston query the color management plugin directly if
heads are compatible for shared-CRTC mode.
Yes, it had occurred to me that this could be particularly painful. :(
The plug-in veto idea, seems like it would be a fairly heavy amount of
overkill if cms-colord would forever be the only plug-in to actually use it.
If I'm understanding all the pieces in play at this point... We'd
really just be giving cms-colord the ability to stop weston from
launching at all, since independent-CRTC cloning doesn't actually work,
and is (quite reasonably!) out of scope for this series and its follow up?
Pretty much, yes... FWIW, the Weston configuration logic is here:
https://gitlab.collabora.com/pq/weston/commit/99d5cf59685c3f98eaf4d31445d29317ebd1087a

The logic starts with drm_heads_changed() which will be called on start
after all KMS resources have been found.

It quits if the output configuration at launch with currently connected
heads does not work out, and it bails out on where it should fall back
to independent-CRTC cloning.
Post by Derek Foreman
Post by Pekka Paalanen
One more idea coming to mind is that the "same-as" directive in
weston.ini could refer to either exclusively shared-CRTC clone mode or
shared-CRTC with fallback to independent-CRTC mode, and we could use a
hypothetical future output layout configuration directives to force
independent-CRTC mode by defining two outputs to show the same area of
the desktop.
Given that, what would you recommend for now?
Can we generate a log message when cms-colord configuration results in
suboptimal display for cloned heads (possibly including some strings to
identify the display/displays that look wrong)?
I suppose... I'd have to look deeper into the CMS API to find out what
to compare. To me this would be a good solution if not even a long-term
solution.
Post by Derek Foreman
Or even a warning when cms-colord and clones are both enabled at the
same time?
I think the CMS plugins are loaded via a generic plugin loading
mechanism, neither Weston nor libweston actually recognizes them
currently.
Post by Derek Foreman
At this point, I think whoever the first person to need clone+cms-colord
simultaneously is going to have to step up and do some heavy lifting.
As long as we're not silently doing something surprising with color
profiles, I think it should be ok? Since clone mode is all new, this
isn't going to cause a regression for anyone.
Yeah.

So, would we be good with initially just documenting in weston-drm man
page for "same-as" directive the issue with CMS? Would you demand the
warning message in the log as well on runtime when there actually is a
problem?

Realistically, whatever you want me to do on this matter has to be a
blocker on the cloning feature. Otherwise I can't promise to do it. :-)


Thanks,
pq
Daniel Stone
2018-02-05 10:29:41 UTC
Permalink
Hi,
Post by Pekka Paalanen
On Fri, 2 Feb 2018 13:40:49 -0600
Post by Derek Foreman
At this point, I think whoever the first person to need clone+cms-colord
simultaneously is going to have to step up and do some heavy lifting.
As long as we're not silently doing something surprising with color
profiles, I think it should be ok? Since clone mode is all new, this
isn't going to cause a regression for anyone.
Makes sense. We can leave the yak unshorn.
Post by Pekka Paalanen
Yeah.
So, would we be good with initially just documenting in weston-drm man
page for "same-as" directive the issue with CMS?
Yep.
Post by Pekka Paalanen
Would you demand the
warning message in the log as well on runtime when there actually is a
problem?
Don't mind.

Cheers,
Daniel, attempting brevity for once
Pekka Paalanen
2017-12-14 11:40:48 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Heads may be disconnected or connected and the compositor needs to be
able to know the state to know which heads to take into use.

Currently a single head is automatically created with an output, and
outputs are only ever created as connected and destroyed on
disconnection, so it suffices to set connected to true. In the future,
backends are expected to create heads for both connected and
disconnected connectors, so that a connector can be forced on without it
being actually connected.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 10 ++++++++
2 files changed, 74 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index b0d1629e..349803e8 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4597,6 +4597,69 @@ weston_head_set_internal(struct weston_head *head)
head->connection_internal = true;
}

+/** Store connector status
+ *
+ * \param head The head to modify.
+ * \param connected Whether the head is connected.
+ *
+ * Connectors are created as disconnected. This function can be used to
+ * set the connector status.
+ *
+ * The status should be set to true when a physical connector is connected to
+ * a video sink device like a monitor and to false when the connector is
+ * disconnected. For nested backends, the connection status should reflect the
+ * connection to the parent display server.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_connection_status(struct weston_head *head, bool connected)
+{
+ head->connected = connected;
+}
+
+/** Is the head currently connected?
+ *
+ * \param head The head to query.
+ * \return Connection status.
+ *
+ * Returns true if the head is physically connected to a monitor, or in
+ * case of a nested backend returns true when there is a connection to the
+ * parent display server.
+ *
+ * This is independent from the head being enabled.
+ *
+ * \sa weston_head_is_enabled
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_connected(struct weston_head *head)
+{
+ return head->connected;
+}
+
+/** Is the head currently enabled?
+ *
+ * \param head The head to query.
+ * \return Video status.
+ *
+ * Returns true if the head is currently transmitting a video stream.
+ *
+ * This is independent of the head being connected.
+ *
+ * \sa weston_head_is_connected
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_enabled(struct weston_head *head)
+{
+ if (!head->output)
+ return false;
+
+ return head->output->enabled;
+}
+
/* Move other outputs when one is resized so the space remains contiguous. */
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
@@ -4968,6 +5031,7 @@ 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);

/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 02b5fa32..d14dd6dc 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -168,6 +168,7 @@ struct weston_head {
bool connection_internal; /**< embedded monitor (e.g. laptop) */

char *name; /**< head name, e.g. connector name */
+ bool connected; /**< is physically connected */
};

struct weston_output {
@@ -1966,8 +1967,17 @@ weston_head_set_subpixel(struct weston_head *head,
enum wl_output_subpixel sp);

void
+weston_head_set_connection_status(struct weston_head *head, bool connected);
+
+void
weston_head_set_internal(struct weston_head *head);

+bool
+weston_head_is_connected(struct weston_head *head);
+
+bool
+weston_head_is_enabled(struct weston_head *head);
+
void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
--
2.13.6
Derek Foreman
2018-02-02 19:52:37 UTC
Permalink
Post by Pekka Paalanen
Heads may be disconnected or connected and the compositor needs to be
able to know the state to know which heads to take into use.
Currently a single head is automatically created with an output, and
outputs are only ever created as connected and destroyed on
disconnection, so it suffices to set connected to true. In the future,
backends are expected to create heads for both connected and
disconnected connectors, so that a connector can be forced on without it
being actually connected.
---
libweston/compositor.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 10 ++++++++
2 files changed, 74 insertions(+)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index b0d1629e..349803e8 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4597,6 +4597,69 @@ weston_head_set_internal(struct weston_head *head)
head->connection_internal = true;
}
+/** Store connector status
+ *
+ * \param head The head to modify.
+ * \param connected Whether the head is connected.
+ *
+ * Connectors are created as disconnected. This function can be used to
+ * set the connector status.
+ *
+ * The status should be set to true when a physical connector is connected to
+ * a video sink device like a monitor and to false when the connector is
+ * disconnected. For nested backends, the connection status should reflect the
+ * connection to the parent display server.
+ *
+ * \memberof weston_head
+ * \internal
+ */
+WL_EXPORT void
+weston_head_set_connection_status(struct weston_head *head, bool connected)
+{
+ head->connected = connected;
+}
+
+/** Is the head currently connected?
+ *
+ * \param head The head to query.
+ * \return Connection status.
+ *
+ * Returns true if the head is physically connected to a monitor, or in
+ * case of a nested backend returns true when there is a connection to the
+ * parent display server.
+ *
+ * This is independent from the head being enabled.
+ *
+ * \sa weston_head_is_enabled
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_connected(struct weston_head *head)
+{
+ return head->connected;
+}
+
+/** Is the head currently enabled?
+ *
+ * \param head The head to query.
+ * \return Video status.
+ *
+ * Returns true if the head is currently transmitting a video stream.
+ *
+ * This is independent of the head being connected.
+ *
+ * \sa weston_head_is_connected
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_enabled(struct weston_head *head)
+{
+ if (!head->output)
+ return false;
+
+ return head->output->enabled;
+}
I suppose if I'm trying to be as pedantic possible, I wasn't expecting
to see this function added from the content of the commit log.

Reviewed-by: Derek Foreman <***@osg.samsung.com>

And if you decide to split weston_head_is_enabled() into its own patch,
that's RB me too. As is the previous "add name to weston_head" patch.

Thanks,
Derek
Post by Pekka Paalanen
+
/* Move other outputs when one is resized so the space remains contiguous. */
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
@@ -4968,6 +5031,7 @@ 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);
/* Add some (in)sane defaults which can be used
* for checking if an output was properly configured
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 02b5fa32..d14dd6dc 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -168,6 +168,7 @@ struct weston_head {
bool connection_internal; /**< embedded monitor (e.g. laptop) */
char *name; /**< head name, e.g. connector name */
+ bool connected; /**< is physically connected */
};
struct weston_output {
@@ -1966,8 +1967,17 @@ weston_head_set_subpixel(struct weston_head *head,
enum wl_output_subpixel sp);
void
+weston_head_set_connection_status(struct weston_head *head, bool connected);
+
+void
weston_head_set_internal(struct weston_head *head);
+bool
+weston_head_is_connected(struct weston_head *head);
+
+bool
+weston_head_is_enabled(struct weston_head *head);
+
void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
Pekka Paalanen
2018-02-05 10:43:04 UTC
Permalink
On Fri, 2 Feb 2018 13:52:37 -0600
Post by Derek Foreman
Post by Pekka Paalanen
Heads may be disconnected or connected and the compositor needs to be
able to know the state to know which heads to take into use.
Currently a single head is automatically created with an output, and
outputs are only ever created as connected and destroyed on
disconnection, so it suffices to set connected to true. In the future,
backends are expected to create heads for both connected and
disconnected connectors, so that a connector can be forced on without it
being actually connected.
---
libweston/compositor.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 10 ++++++++
2 files changed, 74 insertions(+)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index b0d1629e..349803e8 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
+/** Is the head currently enabled?
+ *
+ * \param head The head to query.
+ * \return Video status.
+ *
+ * Returns true if the head is currently transmitting a video stream.
+ *
+ * This is independent of the head being connected.
+ *
+ * \sa weston_head_is_connected
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_enabled(struct weston_head *head)
+{
+ if (!head->output)
+ return false;
+
+ return head->output->enabled;
+}
I suppose if I'm trying to be as pedantic possible, I wasn't expecting
to see this function added from the content of the commit log.
And if you decide to split weston_head_is_enabled() into its own patch,
that's RB me too. As is the previous "add name to weston_head" patch.
Very true.


Thanks,
pq
Pekka Paalanen
2017-12-14 11:40:49 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

weston_compositor needs to maintain a list of all available heads, so
that a compositor can pick and choose which heads to take into or out of
use at arbitrary times. The heads may be on or off, and connected or
disconnected.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 8 ++++++
2 files changed, 83 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 349803e8..99299c9f 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4432,11 +4432,79 @@ weston_head_init(struct weston_head *head, const char *name)
*/
memset(head, 0, sizeof *head);

+ wl_list_init(&head->compositor_link);
wl_list_init(&head->output_link);
wl_list_init(&head->resource_list);
head->name = strdup(name);
}

+/** Register a new head
+ *
+ * \param compositor The compositor.
+ * \param head The head to register, must not be already registered.
+ *
+ * This signals the core that a new head has become available.
+ *
+ * \memberof weston_compositor
+ * \internal
+ */
+static void
+weston_compositor_add_head(struct weston_compositor *compositor,
+ struct weston_head *head)
+{
+ assert(wl_list_empty(&head->compositor_link));
+ assert(head->name);
+
+ wl_list_insert(compositor->head_list.prev, &head->compositor_link);
+ head->compositor = compositor;
+}
+
+/** Iterate over available heads
+ *
+ * \param compositor The compositor.
+ * \param item The iterator, or NULL for start.
+ * \return The next available head in the list.
+ *
+ * Returns all available heads, regardless of being connected or enabled.
+ *
+ * You can iterate over all heads as follows:
+ * \code
+ * struct weston_head *head = NULL;
+ *
+ * while ((head = weston_compositor_iterate_heads(compositor, 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_compositor_iterate_heads(struct weston_compositor *compositor,
+ struct weston_head *iter)
+{
+ struct wl_list *list = &compositor->head_list;
+ struct wl_list *node;
+
+ assert(compositor);
+ assert(!iter || iter->compositor == compositor);
+
+ if (iter)
+ node = iter->compositor_link.next;
+ else
+ node = list->next;
+
+ assert(node);
+ assert(!iter || node != &iter->compositor_link);
+
+ if (node == list)
+ return NULL;
+
+ return container_of(node, struct weston_head, compositor_link);
+}
+
/** Attach a head to an inactive output
*
* \param output The output to attach to.
@@ -4511,6 +4579,8 @@ weston_head_release(struct weston_head *head)
free(head->model);
free(head->serial_number);
free(head->name);
+
+ wl_list_remove(&head->compositor_link);
}

/** Store monitor make, model and serial number
@@ -5032,6 +5102,7 @@ weston_output_init(struct weston_output *output,

weston_head_init(&output->head, name);
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
@@ -5657,6 +5728,7 @@ weston_compositor_create(struct wl_display *display, void *user_data)
wl_list_init(&ec->seat_list);
wl_list_init(&ec->pending_output_list);
wl_list_init(&ec->output_list);
+ wl_list_init(&ec->head_list);
wl_list_init(&ec->key_binding_list);
wl_list_init(&ec->modifier_binding_list);
wl_list_init(&ec->button_binding_list);
@@ -5932,6 +6004,9 @@ weston_compositor_destroy(struct weston_compositor *compositor)
if (compositor->backend)
compositor->backend->destroy(compositor);

+ /* The backend is responsible for destroying the heads. */
+ assert(wl_list_empty(&compositor->head_list));
+
weston_plugin_api_destroy_list(compositor);

free(compositor);
diff --git a/libweston/compositor.h b/libweston/compositor.h
index d14dd6dc..c08c144c 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -153,6 +153,9 @@ enum dpms_enum {
* (windowed nested backends).
*/
struct weston_head {
+ struct weston_compositor *compositor; /**< owning compositor */
+ struct wl_list compositor_link; /**< in weston_compositor::head_list */
+
struct weston_output *output; /**< the output driving this head */
struct wl_list output_link; /**< in weston_output::head_list */

@@ -915,6 +918,7 @@ struct weston_compositor {

struct wl_list pending_output_list;
struct wl_list output_list;
+ struct wl_list head_list; /* struct weston_head::compositor_link */
struct wl_list seat_list;
struct wl_list layer_list; /* struct weston_layer::link */
struct wl_list view_list; /* struct weston_view::link */
@@ -1978,6 +1982,10 @@ weston_head_is_connected(struct weston_head *head);
bool
weston_head_is_enabled(struct weston_head *head);

+struct weston_head *
+weston_compositor_iterate_heads(struct weston_compositor *compositor,
+ struct weston_head *iter);
+
void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:50 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Add a hook for compositors to get a callback when heads are added or
their connection status changes, to which compositors likely want to
react to by enabling or disabling outputs (API for that to be added
later).

As many head changes as possible should be coalesced into a single
heads_changed call. Therefore the callback is made from an idle task.
This anticipates a future atomic output configuration API, where the
global output configuration is tested and set atomically instead of one
by one.

weston_pending_output_coldplug() needs to manually execute the
heads_changed call so that initial outputs are created before any
plugins get their start-up idle tasks ran. This is especially important
for ivi-shell which does not support output hotplug, and for tests to
guarantee the expected outputs.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-
libweston/compositor.h | 8 +++++
2 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 99299c9f..1d436522 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4438,12 +4438,47 @@ weston_head_init(struct weston_head *head, const char *name)
head->name = strdup(name);
}

+/** Idle task for calling heads_changed callback */
+static void
+weston_compositor_call_heads_changed(void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ compositor->heads_changed_source = NULL;
+
+ if (!compositor->heads_changed)
+ return;
+
+ compositor->heads_changed(compositor);
+}
+
+/** Schedule a call on idle to heads_changed callback
+ *
+ * \param compositor The Compositor.
+ *
+ * \memberof weston_compositor
+ * \internal
+ */
+static void
+weston_compositor_schedule_heads_changed(struct weston_compositor *compositor)
+{
+ struct wl_event_loop *loop;
+
+ if (compositor->heads_changed_source)
+ return;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ compositor->heads_changed_source = wl_event_loop_add_idle(loop,
+ weston_compositor_call_heads_changed, compositor);
+}
+
/** Register a new head
*
* \param compositor The compositor.
* \param head The head to register, must not be already registered.
*
- * This signals the core that a new head has become available.
+ * This signals the core that a new head has become available, leading to
+ * heads_changed hook being called later.
*
* \memberof weston_compositor
* \internal
@@ -4457,6 +4492,30 @@ weston_compositor_add_head(struct weston_compositor *compositor,

wl_list_insert(compositor->head_list.prev, &head->compositor_link);
head->compositor = compositor;
+ weston_compositor_schedule_heads_changed(compositor);
+}
+
+/** Set the function to be called when heads change
+ *
+ * \param compositor The compositor.
+ * \param cb The function to call, or NULL for none.
+ *
+ * Setting the function overrides any previous setting.
+ *
+ * The callback function will be called after heads are added or their
+ * connection status has changed. Several changes may be accumulated into a
+ * single call. The user is expected to iterate over the existing heads and
+ * checking their status to find out what changed.
+ *
+ * \sa weston_compositor_iterate_heads, weston_head_is_connected,
+ * weston_head_is_enabled
+ * \memberof weston_compositor
+ */
+WL_EXPORT void
+weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
+ weston_heads_changed_func_t cb)
+{
+ compositor->heads_changed = cb;
}

/** Iterate over available heads
@@ -4680,13 +4739,23 @@ weston_head_set_internal(struct weston_head *head)
* disconnected. For nested backends, the connection status should reflect the
* connection to the parent display server.
*
+ * When the connection status changes, it schedules a call to the heads_changed
+ * hook.
+ *
+ * \sa weston_compositor_set_heads_changed_cb
* \memberof weston_head
* \internal
*/
WL_EXPORT void
weston_head_set_connection_status(struct weston_head *head, bool connected)
{
+ if (head->connected == connected)
+ return;
+
head->connected = connected;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
}

/** Is the head currently connected?
@@ -5296,6 +5365,11 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)

wl_list_for_each_safe(output, next, &compositor->pending_output_list, link)
wl_signal_emit(&compositor->output_pending_signal, output);
+
+ if (compositor->heads_changed_source) {
+ wl_event_source_remove(compositor->heads_changed_source);
+ weston_compositor_call_heads_changed(compositor);
+ }
}

/** Uninitialize an output
@@ -6009,6 +6083,9 @@ weston_compositor_destroy(struct weston_compositor *compositor)

weston_plugin_api_destroy_list(compositor);

+ if (compositor->heads_changed_source)
+ wl_event_source_remove(compositor->heads_changed_source);
+
free(compositor);
}

diff --git a/libweston/compositor.h b/libweston/compositor.h
index c08c144c..f7b7050c 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -883,6 +883,8 @@ struct weston_backend {
struct weston_desktop_xwayland;
struct weston_desktop_xwayland_interface;

+typedef void (*weston_heads_changed_func_t)(struct weston_compositor *compositor);
+
struct weston_compositor {
struct wl_signal destroy_signal;

@@ -978,6 +980,8 @@ struct weston_compositor {
/* Whether to let the compositor run without any input device. */
bool require_input;

+ weston_heads_changed_func_t heads_changed;
+ struct wl_event_source *heads_changed_source;
};

struct weston_buffer {
@@ -1987,6 +1991,10 @@ weston_compositor_iterate_heads(struct weston_compositor *compositor,
struct weston_head *iter);

void
+weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
+ weston_heads_changed_func_t cb);
+
+void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
--
2.13.6
Derek Foreman
2018-02-02 20:31:59 UTC
Permalink
Post by Pekka Paalanen
Add a hook for compositors to get a callback when heads are added or
their connection status changes, to which compositors likely want to
react to by enabling or disabling outputs (API for that to be added
later).
As many head changes as possible should be coalesced into a single
heads_changed call. Therefore the callback is made from an idle task.
This anticipates a future atomic output configuration API, where the
global output configuration is tested and set atomically instead of one
by one.
weston_pending_output_coldplug() needs to manually execute the
heads_changed call so that initial outputs are created before any
plugins get their start-up idle tasks ran. This is especially important
for ivi-shell which does not support output hotplug, and for tests to
guarantee the expected outputs.
Thanks for explaining this here, as I would've been wondering why
otherwise - perhaps a brief comment in the code?
Post by Pekka Paalanen
---
libweston/compositor.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-
libweston/compositor.h | 8 +++++
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 99299c9f..1d436522 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4438,12 +4438,47 @@ weston_head_init(struct weston_head *head, const char *name)
head->name = strdup(name);
}
+/** Idle task for calling heads_changed callback */
+static void
+weston_compositor_call_heads_changed(void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ compositor->heads_changed_source = NULL;
+
+ if (!compositor->heads_changed)
+ return;
+
+ compositor->heads_changed(compositor);
+}
+
+/** Schedule a call on idle to heads_changed callback
+ *
+ * \param compositor The Compositor.
+ *
+ * \memberof weston_compositor
+ * \internal
+ */
+static void
+weston_compositor_schedule_heads_changed(struct weston_compositor *compositor)
+{
+ struct wl_event_loop *loop;
+
+ if (compositor->heads_changed_source)
+ return;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ compositor->heads_changed_source = wl_event_loop_add_idle(loop,
+ weston_compositor_call_heads_changed, compositor);
+}
+
/** Register a new head
*
* \param compositor The compositor.
* \param head The head to register, must not be already registered.
*
- * This signals the core that a new head has become available.
+ * This signals the core that a new head has become available, leading to
+ * heads_changed hook being called later.
*
* \memberof weston_compositor
* \internal
@@ -4457,6 +4492,30 @@ weston_compositor_add_head(struct weston_compositor *compositor,
wl_list_insert(compositor->head_list.prev, &head->compositor_link);
head->compositor = compositor;
+ weston_compositor_schedule_heads_changed(compositor);
+}
+
+/** Set the function to be called when heads change
+ *
+ * \param compositor The compositor.
+ * \param cb The function to call, or NULL for none.
+ *
+ * Setting the function overrides any previous setting.
+ *
+ * The callback function will be called after heads are added or their
+ * connection status has changed. Several changes may be accumulated into a
+ * single call. The user is expected to iterate over the existing heads and
+ * checking their status to find out what changed.
check their statuses

Reviewed-by: Derek Foreman <***@osg.samsung.com>
as is patch 12
Post by Pekka Paalanen
+ *
+ * \sa weston_compositor_iterate_heads, weston_head_is_connected,
+ * weston_head_is_enabled
+ * \memberof weston_compositor
+ */
+WL_EXPORT void
+weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
+ weston_heads_changed_func_t cb)
+{
+ compositor->heads_changed = cb;
}
/** Iterate over available heads
@@ -4680,13 +4739,23 @@ weston_head_set_internal(struct weston_head *head)
* disconnected. For nested backends, the connection status should reflect the
* connection to the parent display server.
*
+ * When the connection status changes, it schedules a call to the heads_changed
+ * hook.
+ *
+ * \sa weston_compositor_set_heads_changed_cb
* \memberof weston_head
* \internal
*/
WL_EXPORT void
weston_head_set_connection_status(struct weston_head *head, bool connected)
{
+ if (head->connected == connected)
+ return;
+
head->connected = connected;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
}
/** Is the head currently connected?
@@ -5296,6 +5365,11 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)
wl_list_for_each_safe(output, next, &compositor->pending_output_list, link)
wl_signal_emit(&compositor->output_pending_signal, output);
+
+ if (compositor->heads_changed_source) {
+ wl_event_source_remove(compositor->heads_changed_source);
+ weston_compositor_call_heads_changed(compositor);
+ }
}
/** Uninitialize an output
@@ -6009,6 +6083,9 @@ weston_compositor_destroy(struct weston_compositor *compositor)
weston_plugin_api_destroy_list(compositor);
+ if (compositor->heads_changed_source)
+ wl_event_source_remove(compositor->heads_changed_source);
+
free(compositor);
}
diff --git a/libweston/compositor.h b/libweston/compositor.h
index c08c144c..f7b7050c 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -883,6 +883,8 @@ struct weston_backend {
struct weston_desktop_xwayland;
struct weston_desktop_xwayland_interface;
+typedef void (*weston_heads_changed_func_t)(struct weston_compositor *compositor);
+
struct weston_compositor {
struct wl_signal destroy_signal;
@@ -978,6 +980,8 @@ struct weston_compositor {
/* Whether to let the compositor run without any input device. */
bool require_input;
+ weston_heads_changed_func_t heads_changed;
+ struct wl_event_source *heads_changed_source;
};
struct weston_buffer {
@@ -1987,6 +1991,10 @@ weston_compositor_iterate_heads(struct weston_compositor *compositor,
struct weston_head *iter);
void
+weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
+ weston_heads_changed_func_t cb);
+
+void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
Pekka Paalanen
2018-02-05 10:47:41 UTC
Permalink
On Fri, 2 Feb 2018 14:31:59 -0600
Post by Derek Foreman
Post by Pekka Paalanen
Add a hook for compositors to get a callback when heads are added or
their connection status changes, to which compositors likely want to
react to by enabling or disabling outputs (API for that to be added
later).
As many head changes as possible should be coalesced into a single
heads_changed call. Therefore the callback is made from an idle task.
This anticipates a future atomic output configuration API, where the
global output configuration is tested and set atomically instead of one
by one.
weston_pending_output_coldplug() needs to manually execute the
heads_changed call so that initial outputs are created before any
plugins get their start-up idle tasks ran. This is especially important
for ivi-shell which does not support output hotplug, and for tests to
guarantee the expected outputs.
Thanks for explaining this here, as I would've been wondering why
otherwise - perhaps a brief comment in the code?
Sure.
Post by Derek Foreman
Post by Pekka Paalanen
---
libweston/compositor.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-
libweston/compositor.h | 8 +++++
2 files changed, 86 insertions(+), 1 deletion(-)
@@ -4457,6 +4492,30 @@ weston_compositor_add_head(struct weston_compositor *compositor,
wl_list_insert(compositor->head_list.prev, &head->compositor_link);
head->compositor = compositor;
+ weston_compositor_schedule_heads_changed(compositor);
+}
+
+/** Set the function to be called when heads change
+ *
+ * \param compositor The compositor.
+ * \param cb The function to call, or NULL for none.
+ *
+ * Setting the function overrides any previous setting.
+ *
+ * The callback function will be called after heads are added or their
+ * connection status has changed. Several changes may be accumulated into a
+ * single call. The user is expected to iterate over the existing heads and
+ * checking their status to find out what changed.
check their statuses
as is patch 12
Yup.


Thanks,
pq
Pekka Paalanen
2017-12-14 11:40:51 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

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 <***@collabora.co.uk>
---
libweston/compositor.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++---
libweston/compositor.h | 78 +++++++++++++++++++++
2 files changed, 256 insertions(+), 9 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 1d436522..a43ee277 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4424,7 +4424,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
@@ -4483,7 +4483,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)
{
@@ -4564,6 +4564,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.
@@ -4583,7 +4629,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)
{
@@ -4593,9 +4639,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);
@@ -4609,14 +4659,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
@@ -4629,7 +4698,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);
@@ -4799,6 +4868,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,
@@ -5170,8 +5264,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
@@ -5408,6 +5505,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
diff --git a/libweston/compositor.h b/libweston/compositor.h
index f7b7050c..f9d034c3 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -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 {
@@ -878,6 +907,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;
@@ -1961,6 +2005,16 @@ 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,
const char *model,
@@ -1986,6 +2040,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);
@@ -1994,6 +2057,21 @@ void
weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
weston_heads_changed_func_t cb);

+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);
--
2.13.6
Derek Foreman
2018-02-02 21:00:09 UTC
Permalink
Post by Pekka Paalanen
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.
---
libweston/compositor.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++---
libweston/compositor.h | 78 +++++++++++++++++++++
2 files changed, 256 insertions(+), 9 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 1d436522..a43ee277 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4424,7 +4424,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
@@ -4483,7 +4483,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)
{
@@ -4564,6 +4564,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.
+ *
+ * \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.
@@ -4583,7 +4629,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)
{
@@ -4593,9 +4639,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);
@@ -4609,14 +4659,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
@@ -4629,7 +4698,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);
@@ -4799,6 +4868,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,
@@ -5170,8 +5264,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
@@ -5408,6 +5505,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);
This took me a couple of reads... if I understand correctly, the full
list of outputs is going to be torn down in the backend
output->destroy()? And we hit that path if we don't have the legacy
allocator_output.

I think the code all looks sound and the API looks reasonable to me, but
I have to concede that I don't know the output handling path enough to
understand weston_compositor_reflow_output's setting up of the
allocator_output.

Acked-by: Derek Foreman <***@osg.samsung.com>

Thanks,
Derek
Post by Pekka Paalanen
+ 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
diff --git a/libweston/compositor.h b/libweston/compositor.h
index f7b7050c..f9d034c3 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -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
+ *
+ *
+ * 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
+ *
+ *
+ * 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 {
@@ -878,6 +907,21 @@ struct weston_backend {
*/
void (*repaint_flush)(struct weston_compositor *compositor,
void *repaint_data);
+
+ /** Allocate a new output
+ *
+ *
+ * Allocates a new output structure that embeds a weston_output,
+ * initializes it, and returns the pointer to the weston_output
+ * member.
+ *
+ */
+ struct weston_output *
+ (*create_output)(struct weston_compositor *compositor,
+ const char *name);
};
struct weston_desktop_xwayland;
@@ -1961,6 +2005,16 @@ 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,
const char *model,
@@ -1986,6 +2040,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);
@@ -1994,6 +2057,21 @@ void
weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
weston_heads_changed_func_t cb);
+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);
Pekka Paalanen
2018-02-05 11:23:29 UTC
Permalink
On Fri, 2 Feb 2018 15:00:09 -0600
Post by Derek Foreman
Post by Pekka Paalanen
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.
---
libweston/compositor.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++---
libweston/compositor.h | 78 +++++++++++++++++++++
2 files changed, 256 insertions(+), 9 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
@@ -4583,7 +4629,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)
{
@@ -4593,9 +4639,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);
+/** 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);
This took me a couple of reads... if I understand correctly, the full
list of outputs is going to be torn down in the backend
output->destroy()? And we hit that path if we don't have the legacy
allocator_output.
Yes, the backend will call weston_output_release() which will detach
all attached heads.

In the legacy backend case, the backend is in charge of creating and
destroying weston_outputs, so we cannot let weston_output_destroy()
call output->destroy(). With legacy frontend things are as they were,
but a migrated frontend will call weston_output_destroy().

In the migrated backend case, weston_outputs are created and destroyed
by the frontend's command.

If we have a migrated frontend and a legacy backend, we just pretend
the frontend is in charge of the weston_output lifetime.

It is enough to check only the first head for the legacy backend case,
because in that case there cannot be more heads attached.
weston_output_attach_head() ensures that.
Post by Derek Foreman
I think the code all looks sound and the API looks reasonable to me, but
I have to concede that I don't know the output handling path enough to
understand weston_compositor_reflow_output's setting up of the
allocator_output.
Why do you mention weston_compositor_reflow_outputs()?

allocator_output is a note in weston_head to say we have a legacy
backend that created a weston_output automatically. So when a migrated
frontend goes to weston_compositor_create_output_with_head() to create
a new weston_output, we can return the (pending) weston_output that
already exists.
Thanks,
pq
Post by Derek Foreman
Post by Pekka Paalanen
+ 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
diff --git a/libweston/compositor.h b/libweston/compositor.h
index f7b7050c..f9d034c3 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -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 */
};
Derek Foreman
2018-02-05 16:13:40 UTC
Permalink
Post by Pekka Paalanen
On Fri, 2 Feb 2018 15:00:09 -0600
Post by Derek Foreman
Post by Pekka Paalanen
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.
---
libweston/compositor.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++---
libweston/compositor.h | 78 +++++++++++++++++++++
2 files changed, 256 insertions(+), 9 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
@@ -4583,7 +4629,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)
{
@@ -4593,9 +4639,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);
+/** 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);
This took me a couple of reads... if I understand correctly, the full
list of outputs is going to be torn down in the backend
output->destroy()? And we hit that path if we don't have the legacy
allocator_output.
Yes, the backend will call weston_output_release() which will detach
all attached heads.
In the legacy backend case, the backend is in charge of creating and
destroying weston_outputs, so we cannot let weston_output_destroy()
call output->destroy(). With legacy frontend things are as they were,
but a migrated frontend will call weston_output_destroy().
In the migrated backend case, weston_outputs are created and destroyed
by the frontend's command.
If we have a migrated frontend and a legacy backend, we just pretend
the frontend is in charge of the weston_output lifetime.
It is enough to check only the first head for the legacy backend case,
because in that case there cannot be more heads attached.
weston_output_attach_head() ensures that.
Post by Derek Foreman
I think the code all looks sound and the API looks reasonable to me, but
I have to concede that I don't know the output handling path enough to
understand weston_compositor_reflow_output's setting up of the
allocator_output.
Why do you mention weston_compositor_reflow_outputs()?
Wow, for really embarrassing reasons actually.
Post by Pekka Paalanen
allocator_output is a note in weston_head to say we have a legacy
backend that created a weston_output automatically. So when a migrated
frontend goes to weston_compositor_create_output_with_head() to create
a new weston_output, we can return the (pending) weston_output that
already exists.
That made sense to me...
After re-reading the patch a couple of times I think I can upgrade this to
Reviewed-by: Derek Foreman <***@osg.samsung.com>

Thanks,
Derek
Post by Pekka Paalanen
Thanks,
pq
Post by Derek Foreman
Post by Pekka Paalanen
+ 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
diff --git a/libweston/compositor.h b/libweston/compositor.h
index f7b7050c..f9d034c3 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -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 */
};
Pekka Paalanen
2017-12-14 11:40:52 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Add support for subscribing to weston_head destruction.

The primary use case for heads being destroyed arbitrarily is the
DRM-backend with MST connectors, which may disappear on unplug. It is
not just the connector becoming disconnected, it is the connector
actually disappearing.

The compositor needs to know about disappearing heads so that it has a
chance to clean up "orphaned" outputs which do get disabled but still
need destroying at run time. Shutdown would destroy them as well.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 9 +++++++++
2 files changed, 54 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index a43ee277..86efcfad 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4433,6 +4433,7 @@ weston_head_init(struct weston_head *head, const char *name)
memset(head, 0, sizeof *head);

wl_list_init(&head->compositor_link);
+ wl_signal_init(&head->destroy_signal);
wl_list_init(&head->output_link);
wl_list_init(&head->resource_list);
head->name = strdup(name);
@@ -4701,6 +4702,8 @@ weston_head_detach(struct weston_head *head)
WL_EXPORT void
weston_head_release(struct weston_head *head)
{
+ wl_signal_emit(&head->destroy_signal, head);
+
weston_head_detach(head);

free(head->make);
@@ -4893,6 +4896,48 @@ weston_head_get_output(struct weston_head *head)
return head->output;
}

+/** Add destroy callback for a head
+ *
+ * \param head The head to watch for.
+ * \param listener The listener to add. The \c notify member must be set.
+ *
+ * Heads may get destroyed for various reasons by the backends. If a head is
+ * attached to an output, the compositor should listen for head destruction
+ * and reconfigure or destroy the output if necessary.
+ *
+ * The destroy callbacks will be called on weston_head destruction before any
+ * automatic detaching from an associated weston_output and before any
+ * weston_head information is lost.
+ *
+ * The \c data argument to the notify callback is the weston_head being
+ * destroyed.
+ */
+WL_EXPORT void
+weston_head_add_destroy_listener(struct weston_head *head,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&head->destroy_signal, listener);
+}
+
+/** Look up destroy listener for a head
+ *
+ * \param head The head to query.
+ * \param notify The notify function used used for the added destroy listener.
+ * \return The listener, or NULL if not found.
+ *
+ * This looks up the previously added destroy listener struct based on the
+ * notify function it has. The listener can be used to access user data
+ * through \c container_of().
+ *
+ * \sa wl_signal_get()
+ */
+WL_EXPORT struct wl_listener *
+weston_head_get_destroy_listener(struct weston_head *head,
+ wl_notify_func_t notify)
+{
+ return wl_signal_get(&head->destroy_signal, notify);
+}
+
/* Move other outputs when one is resized so the space remains contiguous. */
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
diff --git a/libweston/compositor.h b/libweston/compositor.h
index f9d034c3..af61215e 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -155,6 +155,7 @@ enum dpms_enum {
struct weston_head {
struct weston_compositor *compositor; /**< owning compositor */
struct wl_list compositor_link; /**< in weston_compositor::head_list */
+ struct wl_signal destroy_signal; /**< destroy callbacks */

struct weston_output *output; /**< the output driving this head */
struct wl_list output_link; /**< in weston_output::head_list */
@@ -2049,6 +2050,14 @@ weston_head_get_output(struct weston_head *head);
void
weston_head_detach(struct weston_head *head);

+void
+weston_head_add_destroy_listener(struct weston_head *head,
+ struct wl_listener *listener);
+
+struct wl_listener *
+weston_head_get_destroy_listener(struct weston_head *head,
+ wl_notify_func_t notify);
+
struct weston_head *
weston_compositor_iterate_heads(struct weston_compositor *compositor,
struct weston_head *iter);
--
2.13.6
Derek Foreman
2018-02-02 21:13:15 UTC
Permalink
Post by Pekka Paalanen
Add support for subscribing to weston_head destruction.
The primary use case for heads being destroyed arbitrarily is the
DRM-backend with MST connectors, which may disappear on unplug. It is
not just the connector becoming disconnected, it is the connector
actually disappearing.
The compositor needs to know about disappearing heads so that it has a
chance to clean up "orphaned" outputs which do get disabled but still
need destroying at run time. Shutdown would destroy them as well.
---
libweston/compositor.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 9 +++++++++
2 files changed, 54 insertions(+)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index a43ee277..86efcfad 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4433,6 +4433,7 @@ weston_head_init(struct weston_head *head, const char *name)
memset(head, 0, sizeof *head);
wl_list_init(&head->compositor_link);
+ wl_signal_init(&head->destroy_signal);
wl_list_init(&head->output_link);
wl_list_init(&head->resource_list);
head->name = strdup(name);
@@ -4701,6 +4702,8 @@ weston_head_detach(struct weston_head *head)
WL_EXPORT void
weston_head_release(struct weston_head *head)
{
+ wl_signal_emit(&head->destroy_signal, head);
+
weston_head_detach(head);
free(head->make);
@@ -4893,6 +4896,48 @@ weston_head_get_output(struct weston_head *head)
return head->output;
}
+/** Add destroy callback for a head
+ *
+ * \param head The head to watch for.
+ * \param listener The listener to add. The \c notify member must be set.
+ *
+ * Heads may get destroyed for various reasons by the backends. If a head is
+ * attached to an output, the compositor should listen for head destruction
+ * and reconfigure or destroy the output if necessary.
+ *
+ * The destroy callbacks will be called on weston_head destruction before any
+ * automatic detaching from an associated weston_output and before any
+ * weston_head information is lost.
+ *
+ * The \c data argument to the notify callback is the weston_head being
+ * destroyed.
+ */
+WL_EXPORT void
+weston_head_add_destroy_listener(struct weston_head *head,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&head->destroy_signal, listener);
+}
Sure, and we don't need a remove_destroy_listener because the caller
would still have the listener and would just
wl_list_remove(&listener.link) or similar...

This looks good to me
Post by Pekka Paalanen
+/** Look up destroy listener for a head
+ *
+ * \param head The head to query.
+ * \param notify The notify function used used for the added destroy listener.
+ * \return The listener, or NULL if not found.
+ *
+ * This looks up the previously added destroy listener struct based on the
+ * notify function it has. The listener can be used to access user data
+ * through \c container_of().
+ *
+ * \sa wl_signal_get()
+ */
+WL_EXPORT struct wl_listener *
+weston_head_get_destroy_listener(struct weston_head *head,
+ wl_notify_func_t notify)
+{
+ return wl_signal_get(&head->destroy_signal, notify);
+}
+
/* Move other outputs when one is resized so the space remains contiguous. */
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
diff --git a/libweston/compositor.h b/libweston/compositor.h
index f9d034c3..af61215e 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -155,6 +155,7 @@ enum dpms_enum {
struct weston_head {
struct weston_compositor *compositor; /**< owning compositor */
struct wl_list compositor_link; /**< in weston_compositor::head_list */
+ struct wl_signal destroy_signal; /**< destroy callbacks */
struct weston_output *output; /**< the output driving this head */
struct wl_list output_link; /**< in weston_output::head_list */
@@ -2049,6 +2050,14 @@ weston_head_get_output(struct weston_head *head);
void
weston_head_detach(struct weston_head *head);
+void
+weston_head_add_destroy_listener(struct weston_head *head,
+ struct wl_listener *listener);
+
+struct wl_listener *
+weston_head_get_destroy_listener(struct weston_head *head,
+ wl_notify_func_t notify);
+
struct weston_head *
weston_compositor_iterate_heads(struct weston_compositor *compositor,
struct weston_head *iter);
Pekka Paalanen
2017-12-14 11:40:53 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Reacting to DRM hotplug events is racy. It is theoretically possible to
get hotplug events for a quick swap from one monitor to another and
process both only after the new monitor is connected. Hence it is
possible for display device information to change without going through
a disconnected state for the head.

To support such cases, add API to allow detecting it in the compositor.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++----
libweston/compositor.h | 7 ++++
2 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 86efcfad..0d3e185b 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4714,6 +4714,31 @@ weston_head_release(struct weston_head *head)
wl_list_remove(&head->compositor_link);
}

+static void
+weston_head_condition_device_changed(struct weston_head *head, bool condition)
+{
+ if (!condition)
+ return;
+
+ head->device_changed = true;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
+}
+
+/** String non-equal comparison with NULLs being equal */
+static bool
+str_null_neq(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+
+ if (!!a != !!b)
+ return true;
+
+ return strcmp(a, b);
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4724,6 +4749,8 @@ weston_head_release(struct weston_head *head)
* \param serialno The monitor serial number, a made-up string, or NULL for
* none.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4733,6 +4760,11 @@ weston_head_set_monitor_strings(struct weston_head *head,
const char *model,
const char *serialno)
{
+ weston_head_condition_device_changed(head,
+ str_null_neq(head->make, make) ||
+ str_null_neq(head->model, model) ||
+ str_null_neq(head->serial_number, serialno));
+
free(head->make);
free(head->model);
free(head->serial_number);
@@ -4748,6 +4780,8 @@ weston_head_set_monitor_strings(struct weston_head *head,
* \param mm_width Image area width in millimeters.
* \param mm_height Image area height in millimeters.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4755,6 +4789,10 @@ WL_EXPORT void
weston_head_set_physical_size(struct weston_head *head,
int32_t mm_width, int32_t mm_height)
{
+ weston_head_condition_device_changed(head,
+ head->mm_width != mm_width ||
+ head->mm_height != mm_height);
+
head->mm_width = mm_width;
head->mm_height = mm_height;
}
@@ -4770,6 +4808,8 @@ weston_head_set_physical_size(struct weston_head *head,
* - WL_OUTPUT_SUBPIXEL_VERTICAL_RGB,
* - WL_OUTPUT_SUBPIXEL_VERTICAL_BGR
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4777,6 +4817,8 @@ WL_EXPORT void
weston_head_set_subpixel(struct weston_head *head,
enum wl_output_subpixel sp)
{
+ weston_head_condition_device_changed(head, head->subpixel != sp);
+
head->subpixel = sp;
}

@@ -4812,7 +4854,7 @@ weston_head_set_internal(struct weston_head *head)
* connection to the parent display server.
*
* When the connection status changes, it schedules a call to the heads_changed
- * hook.
+ * hook and sets the device_changed flag.
*
* \sa weston_compositor_set_heads_changed_cb
* \memberof weston_head
@@ -4821,13 +4863,9 @@ weston_head_set_internal(struct weston_head *head)
WL_EXPORT void
weston_head_set_connection_status(struct weston_head *head, bool connected)
{
- if (head->connected == connected)
- return;
+ weston_head_condition_device_changed(head, head->connected != connected);

head->connected = connected;
-
- if (head->compositor)
- weston_compositor_schedule_heads_changed(head->compositor);
}

/** Is the head currently connected?
@@ -4871,6 +4909,44 @@ weston_head_is_enabled(struct weston_head *head)
return head->output->enabled;
}

+/** Has the device information changed?
+ *
+ * \param head The head to query.
+ * \return True if the device information has changed since last reset.
+ *
+ * The information about the connected display device, e.g. a monitor, may
+ * change without being disconnected in between. Changing information
+ * causes a call to the heads_changed hook.
+ *
+ * The information includes make, model, serial number, physical size,
+ * and sub-pixel type. The connection status is also included.
+ *
+ * \sa weston_head_reset_device_changed, weston_compositor_set_heads_changed_cb
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_device_changed(struct weston_head *head)
+{
+ return head->device_changed;
+}
+
+/** Acknowledge device information change
+ *
+ * \param head The head to acknowledge.
+ *
+ * Clears the device changed flag on this head. When a compositor has processed
+ * device information, it should call this to be able to notice further
+ * changes.
+ *
+ * \sa weston_head_is_device_changed
+ * \memberof weston_head
+ */
+WL_EXPORT void
+weston_head_reset_device_changed(struct weston_head *head)
+{
+ head->device_changed = false;
+}
+
/** Get the name of a head
*
* \param head The head to query.
diff --git a/libweston/compositor.h b/libweston/compositor.h
index af61215e..1a9aac7f 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -170,6 +170,7 @@ struct weston_head {
char *serial_number; /**< monitor serial */
uint32_t subpixel; /**< enum wl_output_subpixel */
bool connection_internal; /**< embedded monitor (e.g. laptop) */
+ bool device_changed; /**< monitor information has changed */

char *name; /**< head name, e.g. connector name */
bool connected; /**< is physically connected */
@@ -2041,6 +2042,12 @@ weston_head_is_connected(struct weston_head *head);
bool
weston_head_is_enabled(struct weston_head *head);

+bool
+weston_head_is_device_changed(struct weston_head *head);
+
+void
+weston_head_reset_device_changed(struct weston_head *head);
+
const char *
weston_head_get_name(struct weston_head *head);
--
2.13.6
Derek Foreman
2018-02-02 22:25:56 UTC
Permalink
Post by Pekka Paalanen
Reacting to DRM hotplug events is racy. It is theoretically possible to
get hotplug events for a quick swap from one monitor to another and
process both only after the new monitor is connected. Hence it is
possible for display device information to change without going through
a disconnected state for the head.
I figured you'd have to address this somewhere. :)
Post by Pekka Paalanen
To support such cases, add API to allow detecting it in the compositor.
---
libweston/compositor.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++----
libweston/compositor.h | 7 ++++
2 files changed, 89 insertions(+), 6 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 86efcfad..0d3e185b 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4714,6 +4714,31 @@ weston_head_release(struct weston_head *head)
wl_list_remove(&head->compositor_link);
}
+static void
+weston_head_condition_device_changed(struct weston_head *head, bool condition)
+{
+ if (!condition)
+ return;
It feels odd to me to check the condition here instead of having the
callers do something like:
if (condition) weston_head_device_changed();

But I guess that's a style thing, so my personal preference doesn't hold
much weight here...

Either way,
Post by Pekka Paalanen
+
+ head->device_changed = true;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
+}
+
+/** String non-equal comparison with NULLs being equal */
+static bool
+str_null_neq(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+
+ if (!!a != !!b)
+ return true;
+
+ return strcmp(a, b);
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4724,6 +4749,8 @@ weston_head_release(struct weston_head *head)
* \param serialno The monitor serial number, a made-up string, or NULL for
* none.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4733,6 +4760,11 @@ weston_head_set_monitor_strings(struct weston_head *head,
const char *model,
const char *serialno)
{
+ weston_head_condition_device_changed(head,
+ str_null_neq(head->make, make) ||
+ str_null_neq(head->model, model) ||
+ str_null_neq(head->serial_number, serialno));
+
free(head->make);
free(head->model);
free(head->serial_number);
@@ -4748,6 +4780,8 @@ weston_head_set_monitor_strings(struct weston_head *head,
* \param mm_width Image area width in millimeters.
* \param mm_height Image area height in millimeters.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4755,6 +4789,10 @@ WL_EXPORT void
weston_head_set_physical_size(struct weston_head *head,
int32_t mm_width, int32_t mm_height)
{
+ weston_head_condition_device_changed(head,
+ head->mm_width != mm_width ||
+ head->mm_height != mm_height);
+
head->mm_width = mm_width;
head->mm_height = mm_height;
}
@@ -4770,6 +4808,8 @@ weston_head_set_physical_size(struct weston_head *head,
* - WL_OUTPUT_SUBPIXEL_VERTICAL_RGB,
* - WL_OUTPUT_SUBPIXEL_VERTICAL_BGR
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4777,6 +4817,8 @@ WL_EXPORT void
weston_head_set_subpixel(struct weston_head *head,
enum wl_output_subpixel sp)
{
+ weston_head_condition_device_changed(head, head->subpixel != sp);
+
head->subpixel = sp;
}
@@ -4812,7 +4854,7 @@ weston_head_set_internal(struct weston_head *head)
* connection to the parent display server.
*
* When the connection status changes, it schedules a call to the heads_changed
- * hook.
+ * hook and sets the device_changed flag.
*
* \sa weston_compositor_set_heads_changed_cb
* \memberof weston_head
@@ -4821,13 +4863,9 @@ weston_head_set_internal(struct weston_head *head)
WL_EXPORT void
weston_head_set_connection_status(struct weston_head *head, bool connected)
{
- if (head->connected == connected)
- return;
+ weston_head_condition_device_changed(head, head->connected != connected);
head->connected = connected;
-
- if (head->compositor)
- weston_compositor_schedule_heads_changed(head->compositor);
}
/** Is the head currently connected?
@@ -4871,6 +4909,44 @@ weston_head_is_enabled(struct weston_head *head)
return head->output->enabled;
}
+/** Has the device information changed?
+ *
+ * \param head The head to query.
+ * \return True if the device information has changed since last reset.
+ *
+ * The information about the connected display device, e.g. a monitor, may
+ * change without being disconnected in between. Changing information
+ * causes a call to the heads_changed hook.
+ *
+ * The information includes make, model, serial number, physical size,
+ * and sub-pixel type. The connection status is also included.
+ *
+ * \sa weston_head_reset_device_changed, weston_compositor_set_heads_changed_cb
+ * \memberof weston_head
+ */
+WL_EXPORT bool
+weston_head_is_device_changed(struct weston_head *head)
+{
+ return head->device_changed;
+}
+
+/** Acknowledge device information change
+ *
+ * \param head The head to acknowledge.
+ *
+ * Clears the device changed flag on this head. When a compositor has processed
+ * device information, it should call this to be able to notice further
+ * changes.
+ *
+ * \sa weston_head_is_device_changed
+ * \memberof weston_head
+ */
+WL_EXPORT void
+weston_head_reset_device_changed(struct weston_head *head)
+{
+ head->device_changed = false;
+}
+
/** Get the name of a head
*
* \param head The head to query.
diff --git a/libweston/compositor.h b/libweston/compositor.h
index af61215e..1a9aac7f 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -170,6 +170,7 @@ struct weston_head {
char *serial_number; /**< monitor serial */
uint32_t subpixel; /**< enum wl_output_subpixel */
bool connection_internal; /**< embedded monitor (e.g. laptop) */
+ bool device_changed; /**< monitor information has changed */
char *name; /**< head name, e.g. connector name */
bool connected; /**< is physically connected */
@@ -2041,6 +2042,12 @@ weston_head_is_connected(struct weston_head *head);
bool
weston_head_is_enabled(struct weston_head *head);
+bool
+weston_head_is_device_changed(struct weston_head *head);
+
+void
+weston_head_reset_device_changed(struct weston_head *head);
+
const char *
weston_head_get_name(struct weston_head *head);
Pekka Paalanen
2018-02-05 11:34:26 UTC
Permalink
On Fri, 2 Feb 2018 16:25:56 -0600
Post by Derek Foreman
Post by Pekka Paalanen
Reacting to DRM hotplug events is racy. It is theoretically possible to
get hotplug events for a quick swap from one monitor to another and
process both only after the new monitor is connected. Hence it is
possible for display device information to change without going through
a disconnected state for the head.
I figured you'd have to address this somewhere. :)
Post by Pekka Paalanen
To support such cases, add API to allow detecting it in the compositor.
---
libweston/compositor.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++----
libweston/compositor.h | 7 ++++
2 files changed, 89 insertions(+), 6 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 86efcfad..0d3e185b 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4714,6 +4714,31 @@ weston_head_release(struct weston_head *head)
wl_list_remove(&head->compositor_link);
}
+static void
+weston_head_condition_device_changed(struct weston_head *head, bool condition)
+{
+ if (!condition)
+ return;
It feels odd to me to check the condition here instead of having the
if (condition) weston_head_device_changed();
Yeah, it could certainly be done that way. ISTR to go through various
variations, but not sure I tried exactly that though. The starting point
was how weston_head_set_connection_status() was.

I could change that.
Post by Derek Foreman
But I guess that's a style thing, so my personal preference doesn't hold
much weight here...
Either way,
Thanks,
pq
Post by Derek Foreman
Post by Pekka Paalanen
+
+ head->device_changed = true;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
+}
+
+/** String non-equal comparison with NULLs being equal */
+static bool
+str_null_neq(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+
+ if (!!a != !!b)
+ return true;
+
+ return strcmp(a, b);
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4724,6 +4749,8 @@ weston_head_release(struct weston_head *head)
* \param serialno The monitor serial number, a made-up string, or NULL for
* none.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4733,6 +4760,11 @@ weston_head_set_monitor_strings(struct weston_head *head,
const char *model,
const char *serialno)
{
+ weston_head_condition_device_changed(head,
+ str_null_neq(head->make, make) ||
+ str_null_neq(head->model, model) ||
+ str_null_neq(head->serial_number, serialno));
+
free(head->make);
free(head->model);
free(head->serial_number);
@@ -4748,6 +4780,8 @@ weston_head_set_monitor_strings(struct weston_head *head,
* \param mm_width Image area width in millimeters.
* \param mm_height Image area height in millimeters.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4755,6 +4789,10 @@ WL_EXPORT void
weston_head_set_physical_size(struct weston_head *head,
int32_t mm_width, int32_t mm_height)
{
+ weston_head_condition_device_changed(head,
+ head->mm_width != mm_width ||
+ head->mm_height != mm_height);
+
head->mm_width = mm_width;
head->mm_height = mm_height;
}
Derek Foreman
2018-02-05 16:59:12 UTC
Permalink
Post by Pekka Paalanen
On Fri, 2 Feb 2018 16:25:56 -0600
Post by Derek Foreman
Post by Pekka Paalanen
Reacting to DRM hotplug events is racy. It is theoretically possible to
get hotplug events for a quick swap from one monitor to another and
process both only after the new monitor is connected. Hence it is
possible for display device information to change without going through
a disconnected state for the head.
I figured you'd have to address this somewhere. :)
Post by Pekka Paalanen
To support such cases, add API to allow detecting it in the compositor.
---
libweston/compositor.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++----
libweston/compositor.h | 7 ++++
2 files changed, 89 insertions(+), 6 deletions(-)
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 86efcfad..0d3e185b 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4714,6 +4714,31 @@ weston_head_release(struct weston_head *head)
wl_list_remove(&head->compositor_link);
}
+static void
+weston_head_condition_device_changed(struct weston_head *head, bool condition)
+{
+ if (!condition)
+ return;
It feels odd to me to check the condition here instead of having the
if (condition) weston_head_device_changed();
Yeah, it could certainly be done that way. ISTR to go through various
variations, but not sure I tried exactly that though. The starting point
was how weston_head_set_connection_status() was.
I could change that.
I'll concede that it saves a line of code at the call sites, it just
"feels weird" to see constructs like that (in C code, to me). :)

Thanks,
Derek
Post by Pekka Paalanen
Post by Derek Foreman
But I guess that's a style thing, so my personal preference doesn't hold
much weight here...
Either way,
Thanks,
pq
Post by Derek Foreman
Post by Pekka Paalanen
+
+ head->device_changed = true;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
+}
+
+/** String non-equal comparison with NULLs being equal */
+static bool
+str_null_neq(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+
+ if (!!a != !!b)
+ return true;
+
+ return strcmp(a, b);
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4724,6 +4749,8 @@ weston_head_release(struct weston_head *head)
* \param serialno The monitor serial number, a made-up string, or NULL for
* none.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4733,6 +4760,11 @@ weston_head_set_monitor_strings(struct weston_head *head,
const char *model,
const char *serialno)
{
+ weston_head_condition_device_changed(head,
+ str_null_neq(head->make, make) ||
+ str_null_neq(head->model, model) ||
+ str_null_neq(head->serial_number, serialno));
+
free(head->make);
free(head->model);
free(head->serial_number);
@@ -4748,6 +4780,8 @@ weston_head_set_monitor_strings(struct weston_head *head,
* \param mm_width Image area width in millimeters.
* \param mm_height Image area height in millimeters.
*
+ * This may set the device_changed flag.
+ *
* \memberof weston_head
* \internal
*/
@@ -4755,6 +4789,10 @@ WL_EXPORT void
weston_head_set_physical_size(struct weston_head *head,
int32_t mm_width, int32_t mm_height)
{
+ weston_head_condition_device_changed(head,
+ head->mm_width != mm_width ||
+ head->mm_height != mm_height);
+
head->mm_width = mm_width;
head->mm_height = mm_height;
}
Pekka Paalanen
2017-12-14 11:40:54 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Move the call out of wet_configure_windowed_output_from_config() and
into its callers.

This allows to migrate each frontend one by one.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 32fb33e8..995e6f94 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1122,8 +1122,6 @@ wet_configure_windowed_output_from_config(struct weston_output *output,
return -1;
}

- weston_output_enable(output);
-
return 0;
}

@@ -1266,6 +1264,8 @@ headless_backend_output_configure(struct wl_listener *listener, void *data)

if (wet_configure_windowed_output_from_config(output, &defaults) < 0)
weston_log("Cannot configure output \"%s\".\n", output->name);
+
+ weston_output_enable(output);
}

static int
@@ -1484,6 +1484,8 @@ x11_backend_output_configure(struct wl_listener *listener, void *data)

if (wet_configure_windowed_output_from_config(output, &defaults) < 0)
weston_log("Cannot configure output \"%s\".\n", output->name);
+
+ weston_output_enable(output);
}

static int
@@ -1600,6 +1602,8 @@ wayland_backend_output_configure(struct wl_listener *listener, void *data)

if (wet_configure_windowed_output_from_config(output, &defaults) < 0)
weston_log("Cannot configure output \"%s\".\n", output->name);
+
+ weston_output_enable(output);
}

static int
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:55 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Migrate the headless frontend to use the new head-based output
configuration API: listen for heads_changed, and process all heads.

The simple_heads_changed() function is written to be able to cater for
all backends. The rest will be migrated individually.

The head destroy listeners are not exactly necessary, for headless
anyway, but this is an example excercising the API. Also
is_device_changed() check is mostly useful with DRM.

v3: Print "Detected a monitor change" only for enabled heads.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 187 insertions(+), 10 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 995e6f94..0f822f7f 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -74,11 +74,19 @@ struct wet_output_config {
uint32_t transform;
};

+struct wet_compositor;
+
+struct wet_head_tracker {
+ struct wl_listener head_destroy_listener;
+};
+
struct wet_compositor {
struct weston_config *config;
struct wet_output_config *parsed_options;
struct wl_listener pending_output_listener;
bool drm_use_current_mode;
+ int (*simple_output_configure)(struct weston_output *output);
+ bool init_failed;
};

static FILE *weston_logfile = NULL;
@@ -1125,6 +1133,177 @@ wet_configure_windowed_output_from_config(struct weston_output *output,
return 0;
}

+static int
+count_remaining_heads(struct weston_output *output, struct weston_head *to_go)
+{
+ struct weston_head *iter = NULL;
+ int n = 0;
+
+ while ((iter = weston_output_iterate_heads(output, iter))) {
+ if (iter != to_go)
+ n++;
+ }
+
+ return n;
+}
+
+static void
+wet_head_tracker_destroy(struct wet_head_tracker *track)
+{
+ wl_list_remove(&track->head_destroy_listener.link);
+ free(track);
+}
+
+static void
+handle_head_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_head *head = data;
+ struct weston_output *output;
+ struct wet_head_tracker *track =
+ container_of(listener, struct wet_head_tracker,
+ head_destroy_listener);
+
+ wet_head_tracker_destroy(track);
+
+ output = weston_head_get_output(head);
+
+ /* On shutdown path, the output might be already gone. */
+ if (!output)
+ return;
+
+ if (count_remaining_heads(output, head) > 0)
+ return;
+
+ weston_output_destroy(output);
+}
+
+static struct wet_head_tracker *
+wet_head_tracker_from_head(struct weston_head *head)
+{
+ struct wl_listener *lis;
+
+ lis = weston_head_get_destroy_listener(head, handle_head_destroy);
+ if (!lis)
+ return NULL;
+
+ return container_of(lis, struct wet_head_tracker,
+ head_destroy_listener);
+}
+
+/* Listen for head destroy signal.
+ *
+ * If a head is destroyed, we also destroy the associated output.
+ *
+ * Do not bother destroying the head trackers on shutdown, the backend will
+ * destroy the heads which calls our handler to destroy the trackers.
+ */
+static void
+wet_head_tracker_create(struct wet_compositor *compositor,
+ struct weston_head *head)
+{
+ struct wet_head_tracker *track;
+
+ track = zalloc(sizeof *track);
+ if (!track)
+ return;
+
+ track->head_destroy_listener.notify = handle_head_destroy;
+ weston_head_add_destroy_listener(head, &track->head_destroy_listener);
+}
+
+static void
+simple_head_enable(struct weston_compositor *compositor, struct weston_head *head)
+{
+ struct wet_compositor *wet = to_wet_compositor(compositor);
+ struct weston_output *output;
+ int ret = 0;
+
+ output = weston_compositor_create_output_with_head(compositor, head);
+ if (!output) {
+ weston_log("Could not create an output for head \"%s\".\n",
+ weston_head_get_name(head));
+ wet->init_failed = true;
+
+ return;
+ }
+
+ if (wet->simple_output_configure)
+ ret = wet->simple_output_configure(output);
+ if (ret < 0) {
+ weston_log("Cannot configure output \"%s\".\n",
+ weston_head_get_name(head));
+ weston_output_destroy(output);
+ wet->init_failed = true;
+
+ return;
+ }
+
+ if (weston_output_enable(output) < 0) {
+ weston_log("Enabling output \"%s\" failed.\n",
+ weston_head_get_name(head));
+ weston_output_destroy(output);
+ wet->init_failed = true;
+
+ return;
+ }
+
+ wet_head_tracker_create(wet, head);
+
+ /* The weston_compositor will track and destroy the output on exit. */
+}
+
+static void
+simple_head_disable(struct weston_head *head)
+{
+ struct weston_output *output;
+ struct wet_head_tracker *track;
+
+ track = wet_head_tracker_from_head(head);
+ if (track)
+ wet_head_tracker_destroy(track);
+
+ output = weston_head_get_output(head);
+ assert(output);
+ weston_output_destroy(output);
+}
+
+static void
+simple_heads_changed(struct weston_compositor *compositor)
+{
+ struct weston_head *head = NULL;
+ bool connected;
+ bool enabled;
+ bool changed;
+
+ while ((head = weston_compositor_iterate_heads(compositor, head))) {
+ connected = weston_head_is_connected(head);
+ enabled = weston_head_is_enabled(head);
+ changed = weston_head_is_device_changed(head);
+
+ if (connected && !enabled) {
+ simple_head_enable(compositor, head);
+ } else if (!connected && enabled) {
+ simple_head_disable(head);
+ } else if (enabled && changed) {
+ weston_log("Detected a monitor change on head '%s', "
+ "not bothering to do anything about it.\n",
+ weston_head_get_name(head));
+ }
+ weston_head_reset_device_changed(head);
+ }
+}
+
+static void
+wet_set_simple_head_configurator(struct weston_compositor *compositor,
+ int (*fn)(struct weston_output *))
+{
+ struct wet_compositor *wet = to_wet_compositor(compositor);
+
+ wet->simple_output_configure = fn;
+ weston_compositor_set_heads_changed_cb(compositor,
+ simple_heads_changed);
+}
+
static void
configure_input_device(struct weston_compositor *compositor,
struct libinput_device *device)
@@ -1251,10 +1430,9 @@ load_drm_backend(struct weston_compositor *c,
return ret;
}

-static void
-headless_backend_output_configure(struct wl_listener *listener, void *data)
+static int
+headless_backend_output_configure(struct weston_output *output)
{
- struct weston_output *output = data;
struct wet_output_config defaults = {
.width = 1024,
.height = 640,
@@ -1262,10 +1440,7 @@ headless_backend_output_configure(struct wl_listener *listener, void *data)
.transform = WL_OUTPUT_TRANSFORM_NORMAL
};

- if (wet_configure_windowed_output_from_config(output, &defaults) < 0)
- weston_log("Cannot configure output \"%s\".\n", output->name);
-
- weston_output_enable(output);
+ return wet_configure_windowed_output_from_config(output, &defaults);
}

static int
@@ -1303,6 +1478,8 @@ load_headless_backend(struct weston_compositor *c,
config.base.struct_version = WESTON_HEADLESS_BACKEND_CONFIG_VERSION;
config.base.struct_size = sizeof(struct weston_headless_backend_config);

+ wet_set_simple_head_configurator(c, headless_backend_output_configure);
+
/* load the actual wayland backend and configure it */
ret = weston_compositor_load_backend(c, WESTON_BACKEND_HEADLESS,
&config.base);
@@ -1310,8 +1487,6 @@ load_headless_backend(struct weston_compositor *c,
if (ret < 0)
return ret;

- wet_set_pending_output_handler(c, headless_backend_output_configure);
-
if (!no_outputs) {
api = weston_windowed_output_get_api(c);

@@ -1790,7 +1965,7 @@ int main(int argc, char *argv[])
struct wl_client *primary_client;
struct wl_listener primary_client_destroyed;
struct weston_seat *seat;
- struct wet_compositor user_data;
+ struct wet_compositor user_data = { 0 };
int require_input;
int32_t wait_for_debugger = 0;

@@ -1907,6 +2082,8 @@ int main(int argc, char *argv[])
}

weston_pending_output_coldplug(ec);
+ if (user_data.init_failed)
+ goto out;

if (idle_time < 0)
weston_config_section_get_int(section, "idle-time", &idle_time, -1);
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:56 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Migrate the x11 frontend to use the new head-based output configuration
API: listen for heads_changed, and process all heads.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 0f822f7f..f8e84b09 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1646,10 +1646,9 @@ out:
return ret;
}

-static void
-x11_backend_output_configure(struct wl_listener *listener, void *data)
+static int
+x11_backend_output_configure(struct weston_output *output)
{
- struct weston_output *output = data;
struct wet_output_config defaults = {
.width = 1024,
.height = 600,
@@ -1657,10 +1656,7 @@ x11_backend_output_configure(struct wl_listener *listener, void *data)
.transform = WL_OUTPUT_TRANSFORM_NORMAL
};

- if (wet_configure_windowed_output_from_config(output, &defaults) < 0)
- weston_log("Cannot configure output \"%s\".\n", output->name);
-
- weston_output_enable(output);
+ return wet_configure_windowed_output_from_config(output, &defaults);
}

static int
@@ -1696,6 +1692,8 @@ load_x11_backend(struct weston_compositor *c,
config.base.struct_version = WESTON_X11_BACKEND_CONFIG_VERSION;
config.base.struct_size = sizeof(struct weston_x11_backend_config);

+ wet_set_simple_head_configurator(c, x11_backend_output_configure);
+
/* load the actual backend and configure it */
ret = weston_compositor_load_backend(c, WESTON_BACKEND_X11,
&config.base);
@@ -1703,8 +1701,6 @@ load_x11_backend(struct weston_compositor *c,
if (ret < 0)
return ret;

- wet_set_pending_output_handler(c, x11_backend_output_configure);
-
api = weston_windowed_output_get_api(c);

if (!api) {
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:57 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Migrate the Wayland frontend to use the new head-based output
configuration API: listen for heads_changed, and process all heads.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 27 ++++++++-------------------
1 file changed, 8 insertions(+), 19 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index f8e84b09..6eb755e5 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1751,19 +1751,9 @@ load_x11_backend(struct weston_compositor *c,
return 0;
}

-static void
-wayland_backend_output_configure_hotplug(struct wl_listener *listener, void *data)
-{
- struct weston_output *output = data;
-
- /* This backend has all values hardcoded, so nothing can be configured here */
- weston_output_enable(output);
-}
-
-static void
-wayland_backend_output_configure(struct wl_listener *listener, void *data)
+static int
+wayland_backend_output_configure(struct weston_output *output)
{
- struct weston_output *output = data;
struct wet_output_config defaults = {
.width = 1024,
.height = 640,
@@ -1771,10 +1761,7 @@ wayland_backend_output_configure(struct wl_listener *listener, void *data)
.transform = WL_OUTPUT_TRANSFORM_NORMAL
};

- if (wet_configure_windowed_output_from_config(output, &defaults) < 0)
- weston_log("Cannot configure output \"%s\".\n", output->name);
-
- weston_output_enable(output);
+ return wet_configure_windowed_output_from_config(output, &defaults);
}

static int
@@ -1841,13 +1828,15 @@ load_wayland_backend(struct weston_compositor *c,
if (api == NULL) {
/* We will just assume if load_backend() finished cleanly and
* windowed_output_api is not present that wayland backend is
- * started with --sprawl or runs on fullscreen-shell. */
- wet_set_pending_output_handler(c, wayland_backend_output_configure_hotplug);
+ * started with --sprawl or runs on fullscreen-shell.
+ * In this case, all values are hardcoded, so nothing can be
+ * configured; simply create and enable an output. */
+ wet_set_simple_head_configurator(c, NULL);

return 0;
}

- wet_set_pending_output_handler(c, wayland_backend_output_configure);
+ wet_set_simple_head_configurator(c, wayland_backend_output_configure);

section = NULL;
while (weston_config_next_section(wc, &section, &section_name)) {
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:59 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Migrate the RDP frontend to use the new head-based output configuration
API: listen for heads_changed, and process all heads.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 939201b3..55947675 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1502,10 +1502,9 @@ load_headless_backend(struct weston_compositor *c,
return 0;
}

-static void
-rdp_backend_output_configure(struct wl_listener *listener, void *data)
+static int
+rdp_backend_output_configure(struct weston_output *output)
{
- struct weston_output *output = data;
struct wet_compositor *compositor = to_wet_compositor(output->compositor);
struct wet_output_config *parsed_options = compositor->parsed_options;
const struct weston_rdp_output_api *api = weston_rdp_output_get_api(output->compositor);
@@ -1516,7 +1515,7 @@ rdp_backend_output_configure(struct wl_listener *listener, void *data)

if (!api) {
weston_log("Cannot use weston_rdp_output_api.\n");
- return;
+ return -1;
}

if (parsed_options->width)
@@ -1531,10 +1530,10 @@ rdp_backend_output_configure(struct wl_listener *listener, void *data)
if (api->output_set_size(output, width, height) < 0) {
weston_log("Cannot configure output \"%s\" using weston_rdp_output_api.\n",
output->name);
- return;
+ return -1;
}

- weston_output_enable(output);
+ return 0;
}

static void
@@ -1579,14 +1578,14 @@ load_rdp_backend(struct weston_compositor *c,

parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);

+ wet_set_simple_head_configurator(c, rdp_backend_output_configure);
+
ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP,
&config.base);

if (ret < 0)
goto out;

- wet_set_pending_output_handler(c, rdp_backend_output_configure);
-
out:
free(config.bind_address);
free(config.rdp_key);
--
2.13.6
Pekka Paalanen
2017-12-14 11:40:58 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Migrate the fbdev frontend to use the new head-based output
configuration API: listen for heads_changed, and process all heads.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 6eb755e5..939201b3 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1596,10 +1596,9 @@ out:
return ret;
}

-static void
-fbdev_backend_output_configure(struct wl_listener *listener, void *data)
+static int
+fbdev_backend_output_configure(struct weston_output *output)
{
- struct weston_output *output = data;
struct weston_config *wc = wet_get_config(output->compositor);
struct weston_config_section *section;

@@ -1608,7 +1607,7 @@ fbdev_backend_output_configure(struct wl_listener *listener, void *data)
wet_output_set_transform(output, section, WL_OUTPUT_TRANSFORM_NORMAL, UINT32_MAX);
weston_output_set_scale(output, 1);

- weston_output_enable(output);
+ return 0;
}

static int
@@ -1632,6 +1631,8 @@ load_fbdev_backend(struct weston_compositor *c,
config.base.struct_size = sizeof(struct weston_fbdev_backend_config);
config.configure_device = configure_input_device;

+ wet_set_simple_head_configurator(c, fbdev_backend_output_configure);
+
/* load the actual wayland backend and configure it */
ret = weston_compositor_load_backend(c, WESTON_BACKEND_FBDEV,
&config.base);
@@ -1639,8 +1640,6 @@ load_fbdev_backend(struct weston_compositor *c,
if (ret < 0)
goto out;

- wet_set_pending_output_handler(c, fbdev_backend_output_configure);
-
out:
free(config.device);
return ret;
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:00 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Migrate the DRM frontend to use the simple head-based output
configurator, maintaining the exact same features and semantics as
before.

This is an intermediate step. It is unoptimal to create a weston_output
just to turn it off, but the libweston implementation and the DRM
backend require it for now. In the future, the DRM frontend will get its
own configurator that does not create useless weston_outputs and
supports clone mode by attaching multiple heads to the same
weston_output. Clone mode is not yet supported by libweston/DRM.

This is the last frontend migrated, wet_set_pending_output_handler() is
deleted as dead code.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 32 ++++++++++++--------------------
1 file changed, 12 insertions(+), 20 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 55947675..e171e12d 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -83,7 +83,6 @@ struct wet_head_tracker {
struct wet_compositor {
struct weston_config *config;
struct wet_output_config *parsed_options;
- struct wl_listener pending_output_listener;
bool drm_use_current_mode;
int (*simple_output_configure)(struct weston_output *output);
bool init_failed;
@@ -444,16 +443,6 @@ to_wet_compositor(struct weston_compositor *compositor)
return weston_compositor_get_user_data(compositor);
}

-static void
-wet_set_pending_output_handler(struct weston_compositor *ec,
- wl_notify_func_t handler)
-{
- struct wet_compositor *compositor = to_wet_compositor(ec);
-
- compositor->pending_output_listener.notify = handler;
- wl_signal_add(&ec->output_pending_signal, &compositor->pending_output_listener);
-}
-
static struct wet_output_config *
wet_init_parsed_options(struct weston_compositor *ec)
{
@@ -1238,6 +1227,10 @@ simple_head_enable(struct weston_compositor *compositor, struct weston_head *hea
return;
}

+ /* Escape hatch for DRM backend "off" setting. */
+ if (ret > 0)
+ return;
+
if (weston_output_enable(output) < 0) {
weston_log("Enabling output \"%s\" failed.\n",
weston_head_get_name(head));
@@ -1328,10 +1321,9 @@ configure_input_device(struct weston_compositor *compositor,
}
}

-static void
-drm_backend_output_configure(struct wl_listener *listener, void *data)
+static int
+drm_backend_output_configure(struct weston_output *output)
{
- struct weston_output *output = data;
struct weston_config *wc = wet_get_config(output->compositor);
struct wet_compositor *wet = to_wet_compositor(output->compositor);
struct weston_config_section *section;
@@ -1346,7 +1338,7 @@ drm_backend_output_configure(struct wl_listener *listener, void *data)

if (!api) {
weston_log("Cannot use weston_drm_output_api.\n");
- return;
+ return -1;
}

section = weston_config_get_section(wc, "output", "name", output->name);
@@ -1355,7 +1347,7 @@ drm_backend_output_configure(struct wl_listener *listener, void *data)
if (strcmp(s, "off") == 0) {
weston_output_disable(output);
free(s);
- return;
+ return 1;
} else if (wet->drm_use_current_mode || strcmp(s, "current") == 0) {
mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT;
} else if (strcmp(s, "preferred") != 0) {
@@ -1367,7 +1359,7 @@ drm_backend_output_configure(struct wl_listener *listener, void *data)
if (api->set_mode(output, mode, modeline) < 0) {
weston_log("Cannot configure an output using weston_drm_output_api.\n");
free(modeline);
- return;
+ return -1;
}
free(modeline);

@@ -1385,7 +1377,7 @@ drm_backend_output_configure(struct wl_listener *listener, void *data)
api->set_seat(output, seat);
free(seat);

- weston_output_enable(output);
+ return 0;
}

static int
@@ -1419,11 +1411,11 @@ load_drm_backend(struct weston_compositor *c,
config.base.struct_size = sizeof(struct weston_drm_backend_config);
config.configure_device = configure_input_device;

+ wet_set_simple_head_configurator(c, drm_backend_output_configure);
+
ret = weston_compositor_load_backend(c, WESTON_BACKEND_DRM,
&config.base);

- wet_set_pending_output_handler(c, drm_backend_output_configure);
-
free(config.gbm_format);
free(config.seat_id);
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:02 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The signal has been replaced with the heads_changed hook and is no
longer useful.

weston_pending_output_coldplug() is renamed to
weston_compositor_flush_heads_changed() for two reasons: it better
describes what it does now, and it serves as an obvious flag that
libweston ABI has been broken.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 2 +-
libweston/compositor.c | 17 +++++------------
libweston/compositor.h | 3 +--
3 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index e47d7928..14c77107 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -2056,7 +2056,7 @@ int main(int argc, char *argv[])
goto out;
}

- weston_pending_output_coldplug(ec);
+ weston_compositor_flush_heads_changed(ec);
if (user_data.init_failed)
goto out;

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 0d3e185b..a8df7e94 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5408,9 +5408,6 @@ weston_output_init(struct weston_output *output,
* \param output The weston_output object to add
* \param compositor The compositor instance.
*
- * Also notifies the compositor that an output is pending for
- * configuration.
- *
* The opposite of this operation is built into weston_output_release().
*
* \memberof weston_output
@@ -5425,7 +5422,6 @@ weston_compositor_add_pending_output(struct weston_output *output,

wl_list_remove(&output->link);
wl_list_insert(compositor->pending_output_list.prev, &output->link);
- wl_signal_emit(&compositor->output_pending_signal, output);
}

/** Constructs a weston_output object that can be used by the compositor.
@@ -5572,18 +5568,16 @@ weston_output_disable(struct weston_output *output)
output->destroying = 0;
}

-/** Emits a signal to indicate that there are outputs waiting to be configured.
+/** Forces a synchronous call to heads_changed hook
*
* \param compositor The compositor instance
+ *
+ * If there are new or changed heads, calls the heads_changed hook and
+ * returns after the hook returns.
*/
WL_EXPORT void
-weston_pending_output_coldplug(struct weston_compositor *compositor)
+weston_compositor_flush_heads_changed(struct weston_compositor *compositor)
{
- struct weston_output *output, *next;
-
- wl_list_for_each_safe(output, next, &compositor->pending_output_list, link)
- wl_signal_emit(&compositor->output_pending_signal, output);
-
if (compositor->heads_changed_source) {
wl_event_source_remove(compositor->heads_changed_source);
weston_compositor_call_heads_changed(compositor);
@@ -6054,7 +6048,6 @@ weston_compositor_create(struct wl_display *display, void *user_data)
wl_signal_init(&ec->hide_input_panel_signal);
wl_signal_init(&ec->update_input_panel_signal);
wl_signal_init(&ec->seat_created_signal);
- wl_signal_init(&ec->output_pending_signal);
wl_signal_init(&ec->output_created_signal);
wl_signal_init(&ec->output_destroyed_signal);
wl_signal_init(&ec->output_moved_signal);
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 1a9aac7f..af681e51 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -952,7 +952,6 @@ struct weston_compositor {
struct wl_signal update_input_panel_signal;

struct wl_signal seat_created_signal;
- struct wl_signal output_pending_signal;
struct wl_signal output_created_signal;
struct wl_signal output_destroyed_signal;
struct wl_signal output_moved_signal;
@@ -2112,7 +2111,7 @@ void
weston_output_disable(struct weston_output *output);

void
-weston_pending_output_coldplug(struct weston_compositor *compositor);
+weston_compositor_flush_heads_changed(struct weston_compositor *compositor);

struct weston_head *
weston_head_from_resource(struct wl_resource *resource);
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:01 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Rename the function pointer to create_head() because that is what it
does on backends that are converted to the head-based API. Update the
documentation to match.

Surprisingly this is not an ABI break, as the function behaviour and
signature remain intact. Hence API_NAME is not bumped.

This is only an API break, and main.c is fixed accordingly.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 10 +++++-----
libweston/windowed-output-api.h | 23 +++++++++++++----------
2 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index e171e12d..e47d7928 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1487,7 +1487,7 @@ load_headless_backend(struct weston_compositor *c,
return -1;
}

- if (api->output_create(c, "headless") < 0)
+ if (api->create_head(c, "headless") < 0)
return -1;
}

@@ -1715,7 +1715,7 @@ load_x11_backend(struct weston_compositor *c,
continue;
}

- if (api->output_create(c, output_name) < 0) {
+ if (api->create_head(c, output_name) < 0) {
free(output_name);
return -1;
}
@@ -1731,7 +1731,7 @@ load_x11_backend(struct weston_compositor *c,
return -1;
}

- if (api->output_create(c, default_output) < 0) {
+ if (api->create_head(c, default_output) < 0) {
free(default_output);
return -1;
}
@@ -1847,7 +1847,7 @@ load_wayland_backend(struct weston_compositor *c,
continue;
}

- if (api->output_create(c, output_name) < 0) {
+ if (api->create_head(c, output_name) < 0) {
free(output_name);
return -1;
}
@@ -1860,7 +1860,7 @@ load_wayland_backend(struct weston_compositor *c,
if (asprintf(&output_name, "wayland%d", i) < 0)
return -1;

- if (api->output_create(c, output_name) < 0) {
+ if (api->create_head(c, output_name) < 0) {
free(output_name);
return -1;
}
diff --git a/libweston/windowed-output-api.h b/libweston/windowed-output-api.h
index e0f78b4d..388413f3 100644
--- a/libweston/windowed-output-api.h
+++ b/libweston/windowed-output-api.h
@@ -56,23 +56,26 @@ struct weston_windowed_output_api {
int (*output_set_size)(struct weston_output *output,
int width, int height);

- /** Create a new windowed output.
+ /** Create a new windowed head.
*
* \param compositor The compositor instance.
- * \param name Desired name for a new output.
+ * \param name Desired name for a new head, not NULL.
*
* Returns 0 on success, -1 on failure.
*
- * This creates a new output in the backend using this API.
- * After this function is ran, the created output should be
- * ready for configuration using the output_configure() and
- * weston_output_set_{scale,transform}().
+ * This creates a new head in the backend. The new head will
+ * be advertised in the compositor's head list and triggers a
+ * head_changed callback.
*
- * An optional name can be assigned to it, so it can be used
- * by compositor to configure it. It can't be NULL.
+ * A new output can be created for the head. The output must be
+ * configured with output_set_size() and
+ * weston_output_set_{scale,transform}() before enabling it.
+ *
+ * \sa weston_compositor_set_heads_changed_cb(),
+ * weston_compositor_create_output_with_head()
*/
- int (*output_create)(struct weston_compositor *compositor,
- const char *name);
+ int (*create_head)(struct weston_compositor *compositor,
+ const char *name);
};

static inline const struct weston_windowed_output_api *
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:03 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

All frontends have been converted to the new head-based output
management API, which means that
weston_compositor_create_output_with_head() is calling
weston_output_attach_head(). We will never hit the implicit attach
anymore.

Therefore we can now require that an output has at least one head when
calling weston_output_enable(). An output without heads is useless.

The auto-add code is removed as dead.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index a8df7e94..11b9bc4d 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5427,7 +5427,7 @@ weston_compositor_add_pending_output(struct weston_output *output,
/** Constructs a weston_output object that can be used by the compositor.
*
* \param output The weston_output object that needs to be enabled. Must not
- * be enabled already.
+ * be enabled already. Must have at least one head attached.
*
* Output coordinates are calculated and each new output is by default
* assigned to the right of previous one.
@@ -5463,7 +5463,6 @@ weston_output_enable(struct weston_output *output)
struct weston_compositor *c = output->compositor;
struct weston_output *iterator;
int x = 0, y = 0;
- int ret;

if (output->enabled) {
weston_log("Error: attempt to enable an enabled output '%s'\n",
@@ -5471,6 +5470,12 @@ weston_output_enable(struct weston_output *output)
return -1;
}

+ if (wl_list_empty(&output->head_list)) {
+ weston_log("Error: cannot enable output '%s' without heads.\n",
+ output->name);
+ return -1;
+ }
+
iterator = container_of(c->output_list.prev,
struct weston_output, link);

@@ -5499,12 +5504,6 @@ weston_output_enable(struct weston_output *output)
wl_list_init(&output->animation_list);
wl_list_init(&output->feedback_list);

- /* XXX: Temporary until all backends are converted. */
- if (wl_list_empty(&output->head_list)) {
- ret = weston_output_attach_head(output, &output->head);
- assert(ret == 0);
- }
-
/* Enable the output (set up the crtc or create a
* window representing the output, set up the
* renderer, etc)
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:04 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Output make and model are not allowed to be NULL in the protocol, so
ensure they are not forgotten when enabling an output.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 11b9bc4d..903e8809 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5462,6 +5462,7 @@ weston_output_enable(struct weston_output *output)
{
struct weston_compositor *c = output->compositor;
struct weston_output *iterator;
+ struct weston_head *head;
int x = 0, y = 0;

if (output->enabled) {
@@ -5476,6 +5477,11 @@ weston_output_enable(struct weston_output *output)
return -1;
}

+ wl_list_for_each(head, &output->head_list, output_link) {
+ assert(head->make);
+ assert(head->model);
+ }
+
iterator = container_of(c->output_list.prev,
struct weston_output, link);
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:06 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

If the idle_repaint() callback has been scheduled when a weston_output
gets destroyed, the callback will hit use-after-free. I have encountered
this when migrating the wayland backend to the head-based API, using
--sprawl, and closing/disconnecting one of the parent compositor
outputs.

Store the idle_repaint callback source, and destroy it in
weston_output_release(), ensuring we don't get a stale call to
start_repaint_loop later.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 8 +++++++-
libweston/compositor.h | 3 +++
2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index d9185b85..be02ad69 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -2582,6 +2582,7 @@ idle_repaint(void *data)

assert(output->repaint_status == REPAINT_BEGIN_FROM_IDLE);
output->repaint_status = REPAINT_AWAITING_COMPLETION;
+ output->idle_repaint_source = NULL;
output->start_repaint_loop(output);
}

@@ -2706,7 +2707,9 @@ weston_output_schedule_repaint(struct weston_output *output)
return;

output->repaint_status = REPAINT_BEGIN_FROM_IDLE;
- wl_event_loop_add_idle(loop, idle_repaint, output);
+ assert(!output->idle_repaint_source);
+ output->idle_repaint_source = wl_event_loop_add_idle(loop, idle_repaint,
+ output);
TL_POINT("core_repaint_enter_loop", TLP_OUTPUT(output), TLP_END);
}

@@ -5614,6 +5617,9 @@ weston_output_release(struct weston_output *output)

output->destroying = 1;

+ if (output->idle_repaint_source)
+ wl_event_source_remove(output->idle_repaint_source);
+
if (output->enabled)
weston_compositor_remove_output(output);

diff --git a/libweston/compositor.h b/libweston/compositor.h
index af681e51..a9617755 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -216,6 +216,9 @@ struct weston_output {
* next repaint should be run */
struct timespec next_repaint;

+ /** For cancelling the idle_repaint callback on output destruction. */
+ struct wl_event_source *idle_repaint_source;
+
struct weston_output_zoom zoom;
int dirty;
struct wl_signal frame_signal;
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:05 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The functions called here, particularly
weston_output_transform_scale_init(), rely on current mode being set.
The current mode must also be found in the mode list, though we don't
explicitly check it here.

current_mode not being set is a programmer error. It could be a backend
bug, but it could also be a libweston user bug not calling a set size
function.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 903e8809..d9185b85 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5477,6 +5477,12 @@ weston_output_enable(struct weston_output *output)
return -1;
}

+ if (wl_list_empty(&output->mode_list) || !output->current_mode) {
+ weston_log("Error: no video mode for output '%s'.\n",
+ output->name);
+ return -1;
+ }
+
wl_list_for_each(head, &output->head_list, output_link) {
assert(head->make);
assert(head->model);
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:07 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Implement the head-based output API in this backend, and stop relying on
the implicit weston_output::head.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-headless.c | 75 ++++++++++++++++++++++++++++++++++-------
1 file changed, 63 insertions(+), 12 deletions(-)

diff --git a/libweston/compositor-headless.c b/libweston/compositor-headless.c
index 8fbb83ff..3e30e6ac 100644
--- a/libweston/compositor-headless.c
+++ b/libweston/compositor-headless.c
@@ -48,6 +48,10 @@ struct headless_backend {
bool use_pixman;
};

+struct headless_head {
+ struct weston_head base;
+};
+
struct headless_output {
struct weston_output base;

@@ -57,6 +61,12 @@ struct headless_output {
pixman_image_t *image;
};

+static inline struct headless_head *
+to_headless_head(struct weston_head *base)
+{
+ return container_of(base, struct headless_head, base);
+}
+
static inline struct headless_output *
to_headless_output(struct weston_output *base)
{
@@ -185,7 +195,7 @@ headless_output_set_size(struct weston_output *base,
int width, int height)
{
struct headless_output *output = to_headless_output(base);
- struct weston_head *head = &output->base.head;
+ struct weston_head *head;
int output_width, output_height;

/* We can only be called once. */
@@ -194,6 +204,14 @@ headless_output_set_size(struct weston_output *base,
/* Make sure we have scale set. */
assert(output->base.scale);

+ wl_list_for_each(head, &output->base.head_list, output_link) {
+ weston_head_set_monitor_strings(head, "weston", "headless",
+ NULL);
+
+ /* XXX: Calculate proper size. */
+ weston_head_set_physical_size(head, width, height);
+ }
+
output_width = width * output->base.scale;
output_height = height * output->base.scale;

@@ -206,11 +224,6 @@ headless_output_set_size(struct weston_output *base,

output->base.current_mode = &output->mode;

- weston_head_set_monitor_strings(head, "weston", "headless", NULL);
-
- /* XXX: Calculate proper size. */
- weston_head_set_physical_size(head, width, height);
-
output->base.start_repaint_loop = headless_output_start_repaint_loop;
output->base.repaint = headless_output_repaint;
output->base.assign_planes = NULL;
@@ -221,9 +234,8 @@ headless_output_set_size(struct weston_output *base,
return 0;
}

-static int
-headless_output_create(struct weston_compositor *compositor,
- const char *name)
+static struct weston_output *
+headless_output_create(struct weston_compositor *compositor, const char *name)
{
struct headless_output *output;

@@ -231,21 +243,55 @@ headless_output_create(struct weston_compositor *compositor,
assert(name);

output = zalloc(sizeof *output);
- if (output == NULL)
- return -1;
+ if (!output)
+ return NULL;

weston_output_init(&output->base, compositor, name);

output->base.destroy = headless_output_destroy;
output->base.disable = headless_output_disable;
output->base.enable = headless_output_enable;
+ output->base.attach_head = NULL;

weston_compositor_add_pending_output(&output->base, compositor);

+ return &output->base;
+}
+
+static int
+headless_head_create(struct weston_compositor *compositor,
+ const char *name)
+{
+ struct headless_head *head;
+
+ /* name can't be NULL. */
+ assert(name);
+
+ head = zalloc(sizeof *head);
+ if (head == NULL)
+ return -1;
+
+ weston_head_init(&head->base, name);
+ weston_head_set_connection_status(&head->base, true);
+
+ /* Ideally all attributes of the head would be set here, so that the
+ * user has all the information when deciding to create outputs.
+ * We do not have those until set_size() time through.
+ */
+
+ weston_compositor_add_head(compositor, &head->base);
+
return 0;
}

static void
+headless_head_destroy(struct headless_head *head)
+{
+ weston_head_release(&head->base);
+ free(head);
+}
+
+static void
headless_restore(struct weston_compositor *ec)
{
}
@@ -254,15 +300,19 @@ static void
headless_destroy(struct weston_compositor *ec)
{
struct headless_backend *b = to_headless_backend(ec);
+ struct weston_head *base, *next;

weston_compositor_shutdown(ec);

+ wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+ headless_head_destroy(to_headless_head(base));
+
free(b);
}

static const struct weston_windowed_output_api api = {
headless_output_set_size,
- headless_output_create,
+ headless_head_create,
};

static struct headless_backend *
@@ -284,6 +334,7 @@ headless_backend_create(struct weston_compositor *compositor,

b->base.destroy = headless_destroy;
b->base.restore = headless_restore;
+ b->base.create_output = headless_output_create;

b->use_pixman = config->use_pixman;
if (b->use_pixman) {
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:08 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Follow the starndard patttern as the other backends, headless and x11 in
particular, to stop relying on the implicit weston_output::head.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-rdp.c | 64 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 53 insertions(+), 11 deletions(-)

diff --git a/libweston/compositor-rdp.c b/libweston/compositor-rdp.c
index ffad52a6..2f7f885e 100644
--- a/libweston/compositor-rdp.c
+++ b/libweston/compositor-rdp.c
@@ -130,6 +130,10 @@ struct rdp_peers_item {
struct wl_list link;
};

+struct rdp_head {
+ struct weston_head base;
+};
+
struct rdp_output {
struct weston_output base;
struct wl_event_source *finish_frame_timer;
@@ -152,6 +156,12 @@ struct rdp_peer_context {
};
typedef struct rdp_peer_context RdpPeerContext;

+static inline struct rdp_head *
+to_rdp_head(struct weston_head *base)
+{
+ return container_of(base, struct rdp_head, base);
+}
+
static inline struct rdp_output *
to_rdp_output(struct weston_output *base)
{
@@ -482,13 +492,20 @@ rdp_output_set_size(struct weston_output *base,
int width, int height)
{
struct rdp_output *output = to_rdp_output(base);
- struct weston_head *head = &output->base.head;
+ struct weston_head *head;
struct weston_mode *currentMode;
struct weston_mode initMode;

/* We can only be called once. */
assert(!output->base.current_mode);

+ wl_list_for_each(head, &output->base.head_list, output_link) {
+ weston_head_set_monitor_strings(head, "weston", "rdp", NULL);
+
+ /* XXX: Calculate proper size. */
+ weston_head_set_physical_size(head, width, height);
+ }
+
wl_list_init(&output->peers);

initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
@@ -502,11 +519,6 @@ rdp_output_set_size(struct weston_output *base,

output->base.current_mode = output->base.native_mode = currentMode;

- weston_head_set_monitor_strings(head, "weston", "rdp", NULL);
-
- /* XXX: Calculate proper size. */
- weston_head_set_physical_size(head, width, height);
-
output->base.start_repaint_loop = rdp_output_start_repaint_loop;
output->base.repaint = rdp_output_repaint;
output->base.assign_planes = NULL;
@@ -576,27 +588,51 @@ rdp_output_destroy(struct weston_output *base)
free(output);
}

-static int
-rdp_backend_create_output(struct weston_compositor *compositor)
+static struct weston_output *
+rdp_output_create(struct weston_compositor *compositor, const char *name)
{
struct rdp_output *output;

output = zalloc(sizeof *output);
if (output == NULL)
- return -1;
+ return NULL;

- weston_output_init(&output->base, compositor, "rdp");
+ weston_output_init(&output->base, compositor, name);

output->base.destroy = rdp_output_destroy;
output->base.disable = rdp_output_disable;
output->base.enable = rdp_output_enable;
+ output->base.attach_head = NULL;

weston_compositor_add_pending_output(&output->base, compositor);

+ return &output->base;
+}
+
+static int
+rdp_head_create(struct weston_compositor *compositor, const char *name)
+{
+ struct rdp_head *head;
+
+ head = zalloc(sizeof *head);
+ if (!head)
+ return -1;
+
+ weston_head_init(&head->base, name);
+ weston_head_set_connection_status(&head->base, true);
+ weston_compositor_add_head(compositor, &head->base);
+
return 0;
}

static void
+rdp_head_destroy(struct rdp_head *head)
+{
+ weston_head_release(&head->base);
+ free(head);
+}
+
+static void
rdp_restore(struct weston_compositor *ec)
{
}
@@ -605,9 +641,14 @@ static void
rdp_destroy(struct weston_compositor *ec)
{
struct rdp_backend *b = to_rdp_backend(ec);
+ struct weston_head *base, *next;
int i;

weston_compositor_shutdown(ec);
+
+ wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+ rdp_head_destroy(to_rdp_head(base));
+
for (i = 0; i < MAX_FREERDP_FDS; i++)
if (b->listener_events[i])
wl_event_source_remove(b->listener_events[i]);
@@ -1304,6 +1345,7 @@ rdp_backend_create(struct weston_compositor *compositor,
b->compositor = compositor;
b->base.destroy = rdp_destroy;
b->base.restore = rdp_restore;
+ b->base.create_output = rdp_output_create;
b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL;
b->no_clients_resize = config->no_clients_resize;

@@ -1325,7 +1367,7 @@ rdp_backend_create(struct weston_compositor *compositor,
if (pixman_renderer_init(compositor) < 0)
goto err_compositor;

- if (rdp_backend_create_output(compositor) < 0)
+ if (rdp_head_create(compositor, "rdp") < 0)
goto err_compositor;

compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:09 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Destroying the whole output in reenable would cause list walk
corruption: the loop over output_list in session_notify() is not using
wl_list_for_each_safe so output removal would break it.

Creating a new output is also problematic as it needs the compositor to
configure it, but that probably saved us from another list walk failure:
adding the new output to be list while walking the list, possibly
causing it to be destroyed and re-created ad infinitum.

Instead of a complete destroy/create cycle, just do our internal
disable/enable cycle. That will re-open the fbdev, re-read the
parameters, re-create hw_surface, and reinitialize the renderer output.

A problem with this is if fbdev_set_screen_info() fails. We do read the
new parameters, but we don't communicate them to libweston core or old
clients.

However, it is hard to care: to trigger this path, one needs to
VT-switch to another fbdev app which changes the fbdev parameters. That
is quite difficult as VT-switching has been broken for a good while for
fbdev-backend, at least with logind. Also fbdev_set_screen_info() would
have to fail before one should be able to tell something is wrong.

The real reason behind this patch, though, is the migration to the
head-based output API. Destroying and re-creating an output really does
not fit that design. Destroying and re-creating a head would be better,
but again not testable in the current state.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-fbdev.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/libweston/compositor-fbdev.c b/libweston/compositor-fbdev.c
index c71d7eb5..fe79de44 100644
--- a/libweston/compositor-fbdev.c
+++ b/libweston/compositor-fbdev.c
@@ -594,15 +594,15 @@ fbdev_output_reenable(struct fbdev_backend *backend,
struct fbdev_output *output = to_fbdev_output(base);
struct fbdev_screeninfo new_screen_info;
int fb_fd;
- char *device;

weston_log("Re-enabling fbdev output.\n");
+ assert(output->base.enabled);

/* Create the frame buffer. */
fb_fd = fbdev_frame_buffer_open(output->device, &new_screen_info);
if (fb_fd < 0) {
weston_log("Creating frame buffer failed.\n");
- goto err;
+ return -1;
}

/* Check whether the frame buffer details have changed since we were
@@ -616,27 +616,20 @@ fbdev_output_reenable(struct fbdev_backend *backend,

close(fb_fd);

- /* Remove and re-add the output so that resources depending on
+ /* Disable and enable the output so that resources depending on
* the frame buffer X/Y resolution (such as the shadow buffer)
* are re-initialised. */
- device = strdup(output->device);
- fbdev_output_destroy(&output->base);
- fbdev_output_create(backend, device);
- free(device);
-
- return 0;
+ fbdev_output_disable(&output->base);
+ return fbdev_output_enable(&output->base);
}

/* Map the device if it has the same details as before. */
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
weston_log("Mapping frame buffer failed.\n");
- goto err;
+ return -1;
}

return 0;
-
-err:
- return -1;
}

static void
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:11 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Follow the standard pattern set by the headless backend which also uses
the the window output API.

Stops relying on the implicit weston_output::head.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-x11.c | 68 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 54 insertions(+), 14 deletions(-)

diff --git a/libweston/compositor-x11.c b/libweston/compositor-x11.c
index b800132a..2228edbe 100644
--- a/libweston/compositor-x11.c
+++ b/libweston/compositor-x11.c
@@ -115,6 +115,10 @@ struct x11_backend {
} atom;
};

+struct x11_head {
+ struct weston_head base;
+};
+
struct x11_output {
struct weston_output base;

@@ -141,6 +145,12 @@ struct window_delete_data {

struct gl_renderer_interface *gl_renderer;

+static inline struct x11_head *
+to_x11_head(struct weston_head *base)
+{
+ return container_of(base, struct x11_head, base);
+}
+
static inline struct x11_output *
to_x11_output(struct weston_output *base)
{
@@ -1061,7 +1071,7 @@ x11_output_set_size(struct weston_output *base, int width, int height)
{
struct x11_output *output = to_x11_output(base);
struct x11_backend *b = to_x11_backend(base->compositor);
- struct weston_head *head = &output->base.head;
+ struct weston_head *head;
xcb_screen_t *scrn = b->screen;
int output_width, output_height;

@@ -1083,6 +1093,13 @@ x11_output_set_size(struct weston_output *base, int width, int height)
return -1;
}

+ wl_list_for_each(head, &output->base.head_list, output_link) {
+ weston_head_set_monitor_strings(head, "weston-X11", "none", NULL);
+ weston_head_set_physical_size(head,
+ width * scrn->width_in_millimeters / scrn->width_in_pixels,
+ height * scrn->height_in_millimeters / scrn->height_in_pixels);
+ }
+
output_width = width * output->base.scale;
output_height = height * output->base.scale;

@@ -1100,17 +1117,11 @@ x11_output_set_size(struct weston_output *base, int width, int height)
output->base.native_mode = &output->native;
output->base.native_scale = output->base.scale;

- weston_head_set_monitor_strings(head, "weston-X11", "none", NULL);
- weston_head_set_physical_size(head,
- width * scrn->width_in_millimeters / scrn->width_in_pixels,
- height * scrn->height_in_millimeters / scrn->height_in_pixels);
-
return 0;
}

-static int
-x11_output_create(struct weston_compositor *compositor,
- const char *name)
+static struct weston_output *
+x11_output_create(struct weston_compositor *compositor, const char *name)
{
struct x11_output *output;

@@ -1118,22 +1129,46 @@ x11_output_create(struct weston_compositor *compositor,
assert(name);

output = zalloc(sizeof *output);
- if (output == NULL) {
- perror("zalloc");
- return -1;
- }
+ if (!output)
+ return NULL;

weston_output_init(&output->base, compositor, name);

output->base.destroy = x11_output_destroy;
output->base.disable = x11_output_disable;
output->base.enable = x11_output_enable;
+ output->base.attach_head = NULL;

weston_compositor_add_pending_output(&output->base, compositor);

+ return &output->base;
+}
+
+static int
+x11_head_create(struct weston_compositor *compositor, const char *name)
+{
+ struct x11_head *head;
+
+ assert(name);
+
+ head = zalloc(sizeof *head);
+ if (!head)
+ return -1;
+
+ weston_head_init(&head->base, name);
+ weston_head_set_connection_status(&head->base, true);
+ weston_compositor_add_head(compositor, &head->base);
+
return 0;
}

+static void
+x11_head_destroy(struct x11_head *head)
+{
+ weston_head_release(&head->base);
+ free(head);
+}
+
static struct x11_output *
x11_backend_find_output(struct x11_backend *b, xcb_window_t window)
{
@@ -1746,12 +1781,16 @@ static void
x11_destroy(struct weston_compositor *ec)
{
struct x11_backend *backend = to_x11_backend(ec);
+ struct weston_head *base, *next;

wl_event_source_remove(backend->xcb_source);
x11_input_destroy(backend);

weston_compositor_shutdown(ec); /* destroys outputs, too */

+ wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+ x11_head_destroy(to_x11_head(base));
+
XCloseDisplay(backend->dpy);
free(backend);
}
@@ -1775,7 +1814,7 @@ init_gl_renderer(struct x11_backend *b)

static const struct weston_windowed_output_api api = {
x11_output_set_size,
- x11_output_create,
+ x11_head_create,
};

static struct x11_backend *
@@ -1835,6 +1874,7 @@ x11_backend_create(struct weston_compositor *compositor,

b->base.destroy = x11_destroy;
b->base.restore = x11_restore;
+ b->base.create_output = x11_output_create;

if (x11_input_create(b, config->no_input) < 0) {
weston_log("Failed to create X11 input\n");
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:10 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Implement the head-based output API in this backend, and stop relying on
the implicit weston_output::head.

The split between fbdev_head and fbdev_output is somewhat arbitrary.
There is no hotplug or unplug, and there is always 1:1 relationship.
Struct fbdev_screeninfo could have been split as well, but it would not
have made much difference.

I chose fbdev_output to carry the mmap details (buffer_length is now
duplicated here), and fbdev_head to carry the display parameters and
device node path. The device node identifies the head, similar to a
connector.

The backend init creates a head. The compositor uses it to create an
output. Libweston core attaches the head automatically after creating
the output. The attach hook is a suitable place to set up the video
modes on the output as they are dictated by the head, it would be too
late at enable() time.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-fbdev.c | 189 ++++++++++++++++++++++++++++++-------------
1 file changed, 133 insertions(+), 56 deletions(-)

diff --git a/libweston/compositor-fbdev.c b/libweston/compositor-fbdev.c
index fe79de44..5493d846 100644
--- a/libweston/compositor-fbdev.c
+++ b/libweston/compositor-fbdev.c
@@ -78,6 +78,14 @@ struct fbdev_screeninfo {
unsigned int refresh_rate; /* Hertz */
};

+struct fbdev_head {
+ struct weston_head base;
+
+ /* Frame buffer details. */
+ char *device;
+ struct fbdev_screeninfo fb_info;
+};
+
struct fbdev_output {
struct fbdev_backend *backend;
struct weston_output base;
@@ -85,10 +93,9 @@ struct fbdev_output {
struct weston_mode mode;
struct wl_event_source *finish_frame_timer;

- /* Frame buffer details. */
- char *device;
- struct fbdev_screeninfo fb_info;
- void *fb; /* length is fb_info.buffer_length */
+ /* framebuffer mmap details */
+ size_t buffer_length;
+ void *fb;

/* pixman details. */
pixman_image_t *hw_surface;
@@ -96,6 +103,12 @@ struct fbdev_output {

static const char default_seat[] = "seat0";

+static inline struct fbdev_head *
+to_fbdev_head(struct weston_head *base)
+{
+ return container_of(base, struct fbdev_head, base);
+}
+
static inline struct fbdev_output *
to_fbdev_output(struct weston_output *base)
{
@@ -108,6 +121,16 @@ to_fbdev_backend(struct weston_compositor *base)
return container_of(base->backend, struct fbdev_backend, base);
}

+static struct fbdev_head *
+fbdev_output_get_head(struct fbdev_output *output)
+{
+ if (wl_list_length(&output->base.head_list) != 1)
+ return NULL;
+
+ return container_of(output->base.head_list.next,
+ struct fbdev_head, base.output_link);
+}
+
static void
fbdev_output_start_repaint_loop(struct weston_output *output)
{
@@ -368,13 +391,17 @@ fbdev_frame_buffer_open(const char *fb_dev,
static int
fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
{
+ struct fbdev_head *head;
int retval = -1;

+ head = fbdev_output_get_head(output);
+
weston_log("Mapping fbdev frame buffer.\n");

/* Map the frame buffer. Write-only mode, since we don't want to read
* anything back (because it's slow). */
- output->fb = mmap(NULL, output->fb_info.buffer_length,
+ output->buffer_length = head->fb_info.buffer_length;
+ output->fb = mmap(NULL, output->buffer_length,
PROT_WRITE, MAP_SHARED, fd, 0);
if (output->fb == MAP_FAILED) {
weston_log("Failed to mmap frame buffer: %s\n",
@@ -385,11 +412,11 @@ fbdev_frame_buffer_map(struct fbdev_output *output, int fd)

/* Create a pixman image to wrap the memory mapped frame buffer. */
output->hw_surface =
- pixman_image_create_bits(output->fb_info.pixel_format,
- output->fb_info.x_resolution,
- output->fb_info.y_resolution,
+ pixman_image_create_bits(head->fb_info.pixel_format,
+ head->fb_info.x_resolution,
+ head->fb_info.y_resolution,
output->fb,
- output->fb_info.line_length);
+ head->fb_info.line_length);
if (output->hw_surface == NULL) {
weston_log("Failed to create surface for frame buffer.\n");
goto out_unmap;
@@ -400,7 +427,7 @@ fbdev_frame_buffer_map(struct fbdev_output *output, int fd)

out_unmap:
if (retval != 0 && output->fb != NULL) {
- munmap(output->fb, output->fb_info.buffer_length);
+ munmap(output->fb, output->buffer_length);
output->fb = NULL;
}

@@ -425,13 +452,37 @@ fbdev_frame_buffer_unmap(struct fbdev_output *output)
pixman_image_unref(output->hw_surface);
output->hw_surface = NULL;

- if (munmap(output->fb, output->fb_info.buffer_length) < 0)
+ if (munmap(output->fb, output->buffer_length) < 0)
weston_log("Failed to munmap frame buffer: %s\n",
strerror(errno));

output->fb = NULL;
}

+
+static int
+fbdev_output_attach_head(struct weston_output *output_base,
+ struct weston_head *head_base)
+{
+ struct fbdev_output *output = to_fbdev_output(output_base);
+ struct fbdev_head *head = to_fbdev_head(head_base);
+
+ /* Clones not supported. */
+ if (!wl_list_empty(&output->base.head_list))
+ return -1;
+
+ /* only one static mode in list */
+ output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = head->fb_info.x_resolution;
+ output->mode.height = head->fb_info.y_resolution;
+ output->mode.refresh = head->fb_info.refresh_rate;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+ output->base.current_mode = &output->mode;
+
+ return 0;
+}
+
static void fbdev_output_destroy(struct weston_output *base);

static int
@@ -439,11 +490,14 @@ fbdev_output_enable(struct weston_output *base)
{
struct fbdev_output *output = to_fbdev_output(base);
struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
+ struct fbdev_head *head;
int fb_fd;
struct wl_event_loop *loop;

+ head = fbdev_output_get_head(output);
+
/* Create the frame buffer. */
- fb_fd = fbdev_frame_buffer_open(output->device, &output->fb_info);
+ fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
if (fb_fd < 0) {
weston_log("Creating frame buffer failed.\n");
return -1;
@@ -494,64 +548,80 @@ fbdev_output_disable(struct weston_output *base)
return 0;
}

-static int
-fbdev_output_create(struct fbdev_backend *backend,
- const char *device)
+static struct fbdev_head *
+fbdev_head_create(struct fbdev_backend *backend, const char *device)
{
- struct fbdev_output *output;
- struct weston_head *head;
+ struct fbdev_head *head;
int fb_fd;

- weston_log("Creating fbdev output.\n");
-
- output = zalloc(sizeof *output);
- if (output == NULL)
- return -1;
+ head = zalloc(sizeof *head);
+ if (!head)
+ return NULL;

- output->backend = backend;
- output->device = strdup(device);
+ head->device = strdup(device);

/* Create the frame buffer. */
- fb_fd = fbdev_frame_buffer_open(device, &output->fb_info);
+ fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
if (fb_fd < 0) {
- weston_log("Creating frame buffer failed.\n");
+ weston_log("Creating frame buffer head failed.\n");
goto out_free;
}
+ close(fb_fd);

- weston_output_init(&output->base, backend->compositor, "fbdev");
+ weston_head_init(&head->base, "fbdev");
+ weston_head_set_connection_status(&head->base, true);
+ weston_head_set_monitor_strings(&head->base, "unknown",
+ head->fb_info.id, NULL);
+ weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN);
+ weston_head_set_physical_size(&head->base, head->fb_info.width_mm,
+ head->fb_info.height_mm);

- output->base.destroy = fbdev_output_destroy;
- output->base.disable = fbdev_output_disable;
- output->base.enable = fbdev_output_enable;
+ weston_compositor_add_head(backend->compositor, &head->base);

- /* only one static mode in list */
- output->mode.flags =
- WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
- output->mode.width = output->fb_info.x_resolution;
- output->mode.height = output->fb_info.y_resolution;
- output->mode.refresh = output->fb_info.refresh_rate;
- wl_list_insert(&output->base.mode_list, &output->mode.link);
+ weston_log("Created head '%s' for device %s (%s)\n",
+ head->base.name, head->device, head->base.model);

- output->base.current_mode = &output->mode;
+ return head;

- head = &output->base.head;
- weston_head_set_monitor_strings(head, "unknown", output->fb_info.id,
- NULL);
- weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_UNKNOWN);
- weston_head_set_physical_size(head, output->fb_info.width_mm,
- output->fb_info.height_mm);
+out_free:
+ free(head->device);
+ free(head);

- close(fb_fd);
+ return NULL;
+}

- weston_compositor_add_pending_output(&output->base, backend->compositor);
+static void
+fbdev_head_destroy(struct fbdev_head *head)
+{
+ weston_head_release(&head->base);
+ free(head->device);
+ free(head);
+}

- return 0;
+static struct weston_output *
+fbdev_output_create(struct weston_compositor *compositor,
+ const char *name)
+{
+ struct fbdev_output *output;

-out_free:
- free(output->device);
- free(output);
+ weston_log("Creating fbdev output.\n");

- return -1;
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return NULL;
+
+ output->backend = to_fbdev_backend(compositor);
+
+ weston_output_init(&output->base, compositor, "fbdev");
+
+ output->base.destroy = fbdev_output_destroy;
+ output->base.disable = fbdev_output_disable;
+ output->base.enable = fbdev_output_enable;
+ output->base.attach_head = fbdev_output_attach_head;
+
+ weston_compositor_add_pending_output(&output->base, compositor);
+
+ return &output->base;
}

static void
@@ -566,7 +636,6 @@ fbdev_output_destroy(struct weston_output *base)
/* Remove the output. */
weston_output_release(&output->base);

- free(output->device);
free(output);
}

@@ -592,14 +661,17 @@ fbdev_output_reenable(struct fbdev_backend *backend,
struct weston_output *base)
{
struct fbdev_output *output = to_fbdev_output(base);
+ struct fbdev_head *head;
struct fbdev_screeninfo new_screen_info;
int fb_fd;

+ head = fbdev_output_get_head(output);
+
weston_log("Re-enabling fbdev output.\n");
assert(output->base.enabled);

/* Create the frame buffer. */
- fb_fd = fbdev_frame_buffer_open(output->device, &new_screen_info);
+ fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info);
if (fb_fd < 0) {
weston_log("Creating frame buffer failed.\n");
return -1;
@@ -607,9 +679,9 @@ fbdev_output_reenable(struct fbdev_backend *backend,

/* Check whether the frame buffer details have changed since we were
* disabled. */
- if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
+ if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) {
/* Perform a mode-set to restore the old mode. */
- if (fbdev_set_screen_info(fb_fd, &output->fb_info) < 0) {
+ if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) {
weston_log("Failed to restore mode settings. "
"Attempting to re-open output anyway.\n");
}
@@ -636,12 +708,16 @@ static void
fbdev_backend_destroy(struct weston_compositor *base)
{
struct fbdev_backend *backend = to_fbdev_backend(base);
+ struct weston_head *head, *next;

udev_input_destroy(&backend->input);

/* Destroy the output. */
weston_compositor_shutdown(base);

+ wl_list_for_each_safe(head, next, &base->head_list, compositor_link)
+ fbdev_head_destroy(to_fbdev_head(head));
+
/* Chain up. */
weston_launcher_destroy(base->launcher);

@@ -739,6 +815,7 @@ fbdev_backend_create(struct weston_compositor *compositor,

backend->base.destroy = fbdev_backend_destroy;
backend->base.restore = fbdev_restore;
+ backend->base.create_output = fbdev_output_create;

backend->prev_state = WESTON_COMPOSITOR_ACTIVE;

@@ -747,7 +824,7 @@ fbdev_backend_create(struct weston_compositor *compositor,
if (pixman_renderer_init(compositor) < 0)
goto out_launcher;

- if (fbdev_output_create(backend, param->device) < 0)
+ if (!fbdev_head_create(backend, param->device))
goto out_launcher;

udev_input_init(&backend->input, compositor, backend->udev,
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:12 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Add safeguards to make it painfully obvious if we ever get the pairing
of wayland_backend_create_output_surface() and
wayland_backend_destroy_output_surface() wrong. Helps catching bugs.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-wayland.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c
index 040c40b9..39e219a1 100644
--- a/libweston/compositor-wayland.c
+++ b/libweston/compositor-wayland.c
@@ -645,16 +645,25 @@ wayland_output_repaint_pixman(struct weston_output *output_base,
static void
wayland_backend_destroy_output_surface(struct wayland_output *output)
{
- if (output->parent.xdg_toplevel)
+ assert(output->parent.surface);
+
+ if (output->parent.xdg_toplevel) {
zxdg_toplevel_v6_destroy(output->parent.xdg_toplevel);
+ output->parent.xdg_toplevel = NULL;
+ }

- if (output->parent.xdg_surface)
+ if (output->parent.xdg_surface) {
zxdg_surface_v6_destroy(output->parent.xdg_surface);
+ output->parent.xdg_surface = NULL;
+ }

- if (output->parent.shell_surface)
+ if (output->parent.shell_surface) {
wl_shell_surface_destroy(output->parent.shell_surface);
+ output->parent.shell_surface = NULL;
+ }

wl_surface_destroy(output->parent.surface);
+ output->parent.surface = NULL;
}

static void
@@ -1139,6 +1148,8 @@ wayland_backend_create_output_surface(struct wayland_output *output)
{
struct wayland_backend *b = to_wayland_backend(output->base.compositor);

+ assert(!output->parent.surface);
+
output->parent.surface =
wl_compositor_create_surface(b->parent.compositor);
if (!output->parent.surface)
--
2.13.6
Pekka Paalanen
2017-12-14 11:41:13 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Follow the standard pattern used in the headless and x11 backend
migration, but also cater for the two other backend modes: --sprawl or
fullscreen-shell, and --fullscreen.

Stops relying on the implicit weston_output::head.

Unlike other backends, this uses the attach_head hook to do the
required head setup that is not possible to do without an output, but
must be done before weston_output_enable(). This also requires the
detach_head hook for the one case where the user attaches a --fullscreen
head and then detaches it without enabling the output.

It is a little awkward to fully initialize heads as late as attach, but
aside from the --sprawl/fullscreen-shell case, there is not really a way
to know the head properties without creating the parent wl_surface and
configuring it.

Heads/outputs created for parent outputs now have distinct names instead
of all being called "wlparent".

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-wayland.c | 239 ++++++++++++++++++++++++++++++-----------
1 file changed, 178 insertions(+), 61 deletions(-)

diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c
index 39e219a1..348f9091 100644
--- a/libweston/compositor-wayland.c
+++ b/libweston/compositor-wayland.c
@@ -137,7 +137,7 @@ struct wayland_output {

struct wayland_parent_output {
struct wayland_backend *backend; /**< convenience */
- struct wayland_output *output;
+ struct wayland_head *head;
struct wl_list link;

struct wl_output *global;
@@ -161,6 +161,11 @@ struct wayland_parent_output {
struct weston_mode *current_mode;
};

+struct wayland_head {
+ struct weston_head base;
+ struct wayland_parent_output *parent_output;
+};
+
struct wayland_shm_buffer {
struct wayland_output *output;
struct wl_list link;
@@ -210,6 +215,12 @@ struct wayland_input {

struct gl_renderer_interface *gl_renderer;

+static inline struct wayland_head *
+to_wayland_head(struct weston_head *base)
+{
+ return container_of(base, struct wayland_head, base);
+}
+
static inline struct wayland_output *
to_wayland_output(struct weston_output *base)
{
@@ -1275,9 +1286,59 @@ err_output:
return -1;
}

-static struct wayland_output *
-wayland_output_create_common(struct weston_compositor *compositor,
- const char *name)
+static int
+wayland_output_setup_for_parent_output(struct wayland_output *output,
+ struct wayland_parent_output *poutput);
+
+static int
+wayland_output_setup_fullscreen(struct wayland_output *output,
+ struct wayland_head *head);
+
+static int
+wayland_output_attach_head(struct weston_output *output_base,
+ struct weston_head *head_base)
+{
+ struct wayland_backend *b = to_wayland_backend(output_base->compositor);
+ struct wayland_output *output = to_wayland_output(output_base);
+ struct wayland_head *head = to_wayland_head(head_base);
+
+ if (!wl_list_empty(&output->base.head_list))
+ return -1;
+
+ if (head->parent_output) {
+ if (wayland_output_setup_for_parent_output(output,
+ head->parent_output) < 0)
+ return -1;
+ } else if (b->fullscreen) {
+ if (wayland_output_setup_fullscreen(output, head) < 0)
+ return -1;
+ } else {
+ /* A floating window, nothing to do. */
+ }
+
+ return 0;
+}
+
+static void
+wayland_output_detach_head(struct weston_output *output_base,
+ struct weston_head *head)
+{
+ struct wayland_output *output = to_wayland_output(output_base);
+
+ /* Rely on the disable hook if the output was enabled. We do not
+ * support cloned heads, so detaching is guaranteed to disable the
+ * output.
+ */
+ if (output->base.enabled)
+ return;
+
+ /* undo setup fullscreen */
+ if (output->parent.surface)
+ wayland_backend_destroy_output_surface(output);
+}
+
+static struct weston_output *
+wayland_output_create(struct weston_compositor *compositor, const char *name)
{
struct wayland_output *output;
char *title;
@@ -1302,29 +1363,87 @@ wayland_output_create_common(struct weston_compositor *compositor,
output->base.destroy = wayland_output_destroy;
output->base.disable = wayland_output_disable;
output->base.enable = wayland_output_enable;
+ output->base.attach_head = wayland_output_attach_head;
+ output->base.detach_head = wayland_output_detach_head;
+
+ weston_compositor_add_pending_output(&output->base, compositor);
+
+ return &output->base;
+}
+
+static struct wayland_head *
+wayland_head_create(struct weston_compositor *compositor, const char *name)
+{
+ struct wayland_head *head;
+
+ assert(name);
+
+ head = zalloc(sizeof *head);
+ if (!head)
+ return NULL;
+
+ weston_head_init(&head->base, name);
+ weston_head_set_connection_status(&head->base, true);
+ weston_compositor_add_head(compositor, &head->base);

- return output;
+ return head;
}

static int
-wayland_output_create(struct weston_compositor *compositor, const char *name)
+wayland_head_create_windowed(struct weston_compositor *compositor,
+ const char *name)
{
- struct wayland_output *output;
+ if (!wayland_head_create(compositor, name))
+ return -1;

- output = wayland_output_create_common(compositor, name);
- if (!output)
+ return 0;
+}
+
+static int
+wayland_head_create_for_parent_output(struct weston_compositor *compositor,
+ struct wayland_parent_output *poutput)
+{
+ struct wayland_head *head;
+ char name[100];
+ int ret;
+
+ ret = snprintf(name, sizeof(name), "wlparent-%d", poutput->id);
+ if (ret < 1 || (unsigned)ret >= sizeof(name))
return -1;

- weston_compositor_add_pending_output(&output->base, compositor);
+ head = wayland_head_create(compositor, name);
+ if (!head)
+ return -1;
+
+ assert(!poutput->head);
+ head->parent_output = poutput;
+ poutput->head = head;
+
+ weston_head_set_monitor_strings(&head->base,
+ poutput->physical.make,
+ poutput->physical.model, NULL);
+ weston_head_set_physical_size(&head->base,
+ poutput->physical.width,
+ poutput->physical.height);

return 0;
}

+static void
+wayland_head_destroy(struct wayland_head *head)
+{
+ if (head->parent_output)
+ head->parent_output->head = NULL;
+
+ weston_head_release(&head->base);
+ free(head);
+}
+
static int
wayland_output_set_size(struct weston_output *base, int width, int height)
{
struct wayland_output *output = to_wayland_output(base);
- struct weston_head *head = &output->base.head;
+ struct weston_head *head;
int output_width, output_height;

/* We can only be called once. */
@@ -1345,6 +1464,13 @@ wayland_output_set_size(struct weston_output *base, int width, int height)
return -1;
}

+ wl_list_for_each(head, &output->base.head_list, output_link) {
+ weston_head_set_monitor_strings(head, "wayland", "none", NULL);
+
+ /* XXX: Calculate proper size. */
+ weston_head_set_physical_size(head, width, height);
+ }
+
output_width = width * output->base.scale;
output_height = height * output->base.scale;

@@ -1358,25 +1484,15 @@ wayland_output_set_size(struct weston_output *base, int width, int height)

output->base.current_mode = &output->mode;

- weston_head_set_monitor_strings(head, "wayland", "none", NULL);
-
- /* XXX: Calculate proper size. */
- weston_head_set_physical_size(head, width, height);
-
return 0;
}

static int
-wayland_output_create_for_parent_output(struct wayland_backend *b,
- struct wayland_parent_output *poutput)
+wayland_output_setup_for_parent_output(struct wayland_output *output,
+ struct wayland_parent_output *poutput)
{
- struct wayland_output *output;
struct weston_mode *mode;

- output = wayland_output_create_common(b->compositor, "wlparent");
- if (!output)
- return -1;
-
if (poutput->current_mode) {
mode = poutput->current_mode;
} else if (poutput->preferred_mode) {
@@ -1386,7 +1502,7 @@ wayland_output_create_for_parent_output(struct wayland_backend *b,
struct weston_mode, link);
} else {
weston_log("No valid modes found. Skipping output.\n");
- goto out;
+ return -1;
}

output->base.scale = 1;
@@ -1394,13 +1510,6 @@ wayland_output_create_for_parent_output(struct wayland_backend *b,

output->parent.output = poutput->global;

- weston_head_set_monitor_strings(&output->base.head,
- poutput->physical.make,
- poutput->physical.model, NULL);
- weston_head_set_physical_size(&output->base.head,
- poutput->physical.width,
- poutput->physical.height);
-
wl_list_insert_list(&output->base.mode_list, &poutput->mode_list);
wl_list_init(&poutput->mode_list);

@@ -1410,33 +1519,21 @@ wayland_output_create_for_parent_output(struct wayland_backend *b,

/* output->mode is unused in this path. */

- weston_compositor_add_pending_output(&output->base, b->compositor);
-
return 0;
-
-out:
- weston_output_release(&output->base);
- free(output->title);
- free(output);
-
- return -1;
}

static int
-wayland_output_create_fullscreen(struct wayland_backend *b)
+wayland_output_setup_fullscreen(struct wayland_output *output,
+ struct wayland_head *head)
{
- struct wayland_output *output;
+ struct wayland_backend *b = to_wayland_backend(output->base.compositor);
int width = 0, height = 0;

- output = wayland_output_create_common(b->compositor, "wayland-fullscreen");
- if (!output)
- return -1;
-
output->base.scale = 1;
output->base.transform = WL_OUTPUT_TRANSFORM_NORMAL;

if (wayland_backend_create_output_surface(output) < 0)
- goto err_surface;
+ return -1;

/* What should size be set if conditional is false? */
if (b->parent.xdg_shell || b->parent.shell) {
@@ -1456,16 +1553,15 @@ wayland_output_create_fullscreen(struct wayland_backend *b)
if (wayland_output_set_size(&output->base, width, height) < 0)
goto err_set_size;

- weston_compositor_add_pending_output(&output->base, b->compositor);
+ /* The head is not attached yet, so set_size did not set these. */
+ weston_head_set_monitor_strings(&head->base, "wayland", "none", NULL);
+ /* XXX: Calculate proper size. */
+ weston_head_set_physical_size(&head->base, width, height);

return 0;

err_set_size:
wayland_backend_destroy_output_surface(output);
-err_surface:
- weston_output_release(&output->base);
- free(output->title);
- free(output);

return -1;
}
@@ -2270,16 +2366,32 @@ find_mode(struct wl_list *list, int32_t width, int32_t height, uint32_t refresh)
return mode;
}

+static struct weston_output *
+wayland_parent_output_get_enabled_output(struct wayland_parent_output *poutput)
+{
+ struct wayland_head *head = poutput->head;
+
+ if (!head)
+ return NULL;
+
+ if (!weston_head_is_enabled(&head->base))
+ return NULL;
+
+ return weston_head_get_output(&head->base);
+}
+
static void
wayland_parent_output_mode(void *data, struct wl_output *wl_output_proxy,
uint32_t flags, int32_t width, int32_t height,
int32_t refresh)
{
struct wayland_parent_output *output = data;
+ struct weston_output *enabled_output;
struct weston_mode *mode;

- if (output->output) {
- mode = find_mode(&output->output->base.mode_list,
+ enabled_output = wayland_parent_output_get_enabled_output(output);
+ if (enabled_output) {
+ mode = find_mode(&enabled_output->mode_list,
width, height, refresh);
if (!mode)
return;
@@ -2313,7 +2425,7 @@ output_sync_callback(void *data, struct wl_callback *callback, uint32_t unused)

assert(output->backend->sprawl_across_outputs);

- wayland_output_create_for_parent_output(output->backend, output);
+ wayland_head_create_for_parent_output(output->backend->compositor, output);
}

static const struct wl_callback_listener output_sync_listener = {
@@ -2361,8 +2473,8 @@ wayland_parent_output_destroy(struct wayland_parent_output *output)
if (output->sync_cb)
wl_callback_destroy(output->sync_cb);

- if (output->output)
- wayland_output_destroy(&output->output->base);
+ if (output->head)
+ wayland_head_destroy(output->head);

wl_output_destroy(output->global);
free(output->physical.make);
@@ -2472,11 +2584,15 @@ static void
wayland_destroy(struct weston_compositor *ec)
{
struct wayland_backend *b = to_wayland_backend(ec);
+ struct weston_head *base, *next;

wl_event_source_remove(b->parent.wl_source);

weston_compositor_shutdown(ec);

+ wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+ wayland_head_destroy(to_wayland_head(base));
+
if (b->parent.shm)
wl_shm_destroy(b->parent.shm);

@@ -2632,6 +2748,7 @@ wayland_backend_create(struct weston_compositor *compositor,

b->base.destroy = wayland_destroy;
b->base.restore = wayland_restore;
+ b->base.create_output = wayland_output_create;

loop = wl_display_get_event_loop(compositor->wl_display);

@@ -2676,7 +2793,7 @@ wayland_backend_destroy(struct wayland_backend *b)

static const struct weston_windowed_output_api windowed_api = {
wayland_output_set_size,
- wayland_output_create,
+ wayland_head_create_windowed,
};

static void
@@ -2713,14 +2830,14 @@ weston_backend_init(struct weston_compositor *compositor,
wl_display_roundtrip(b->parent.wl_display);

wl_list_for_each(poutput, &b->parent.output_list, link)
- wayland_output_create_for_parent_output(b, poutput);
+ wayland_head_create_for_parent_output(compositor, poutput);

return 0;
}

if (new_config.fullscreen) {
- if (wayland_output_create_fullscreen(b) < 0) {
- weston_log("Unable to create a fullscreen output.\n");
+ if (!wayland_head_create(compositor, "wayland-fullscreen")) {
+ weston_log("Unable to create a fullscreen head.\n");
goto err_outputs;
}
--
2.13.6
Alex Deucher
2017-12-14 15:11:32 UTC
Permalink
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.


Alex
Post by Pekka Paalanen
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5
I went through the same testing procedure as with v3, the previous submission.
https://gitlab.collabora.com/pq/weston/commits/clonemode-4
- support for configuring clone mode in weston.ini
- main.c implementation to configure a clode using the new API
- desktop-shell enhancements to avoid redundant panel and background surfaces
in clone mode
- hooking up custom data to weston_output via a new user-side destroy signal
- naming outputs freely
- DRM-backend support for shared-CRTC clone mode
- video mode list merging in the DRM-backend
The shared-CRTC clone mode has been tested to work on an i.MX6 device.
Unfortunately it seems that PC hardware that would support it is becoming
scarce AFAIU.
Most of the patches in clonemode-4 depend on the atomic modesetting series and
can therefore be submitted only after that one.
Thanks,
pq
libweston: introduce weston_head
libweston: move wl_output to weston_head
libweston: use head in wl_output global
libweston: make wl_output point to weston_head
libweston: refactor weston_mode_switch_finish
libweston: introduce weston_output::head_list
libweston: properly orphan wl_output resources
libweston: strdup head make, model, serial_number
cms-colord: find a good head
libweston: add name to weston_head
libweston: add weston_head::connected
libweston: add compositor list of heads
libweston: add heads_changed hook
libweston: new head-based output management API
libweston: add weston_head destroy signal
libweston: add weston_head_is_device_changed() API
weston: move weston_output_enable() into callers
weston: migrate headless to head-based output API
weston: migrate x11 to head-based output API
weston: migrate wayland to head-based output API
weston: migrate fbdev to head-based output API
weston: migrate RDP to head-based output API
weston: migrate DRM to head-based output API
libweston: change windowed_output_api output_create to create_head
libweston: remove output_pending_signal
libweston: stop auto-adding the implicit head
libweston: assert make/model in weston_output_enable()
libweston: assert current_mode in weston_output_enable()
libweston: cancel idle_repaint on output destroy
compositor-headless: migrate to head-based output API
compositor-rdp: migrate to head-based output API
compositor-fbdev: make re-enable less drastic
compositor-fbdev: migrate to head-based output API
compositor-x11: migrate to head-based output API
compositor-wayland: strict surface create/destroy
compositor-wayland: migrate to head-based output API
compositor/cms-colord.c | 38 +-
compositor/main.c | 304 +++++++++---
compositor/weston-screenshooter.c | 2 +-
desktop-shell/input-panel.c | 4 +-
desktop-shell/shell.c | 4 +-
fullscreen-shell/fullscreen-shell.c | 4 +-
ivi-shell/input-panel-ivi.c | 4 +-
libweston-desktop/wl-shell.c | 2 +-
libweston-desktop/xdg-shell-v5.c | 2 +-
libweston-desktop/xdg-shell-v6.c | 2 +-
libweston/compositor-drm.c | 15 +-
libweston/compositor-fbdev.c | 206 +++++---
libweston/compositor-headless.c | 75 ++-
libweston/compositor-rdp.c | 64 ++-
libweston/compositor-wayland.c | 254 +++++++---
libweston/compositor-x11.c | 71 ++-
libweston/compositor.c | 948 +++++++++++++++++++++++++++++++++---
libweston/compositor.h | 182 ++++++-
libweston/windowed-output-api.h | 23 +-
tests/weston-test.c | 2 +-
20 files changed, 1832 insertions(+), 374 deletions(-)
--
2.13.6
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2017-12-15 07:27:03 UTC
Permalink
On Thu, 14 Dec 2017 10:11:32 -0500
Post by Alex Deucher
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.
Hi Alex,

that's cool. How does userspace take advantage of that, how do you
configure KMS/atomic to make use of that? Is it supported through
legacy KMS (non-atomic) as well?

Does it involve things like the driver stealing an unused CRTC for each
additional clone?

The feature would fit perfectly under the "shared-CRTC clone mode" from
libweston API point of view, even though it's not actually a single
shared CRTC.


Thanks,
pq
Post by Alex Deucher
Alex
Post by Pekka Paalanen
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5
I went through the same testing procedure as with v3, the previous submission.
https://gitlab.collabora.com/pq/weston/commits/clonemode-4
- support for configuring clone mode in weston.ini
- main.c implementation to configure a clode using the new API
- desktop-shell enhancements to avoid redundant panel and background surfaces
in clone mode
- hooking up custom data to weston_output via a new user-side destroy signal
- naming outputs freely
- DRM-backend support for shared-CRTC clone mode
- video mode list merging in the DRM-backend
The shared-CRTC clone mode has been tested to work on an i.MX6 device.
Unfortunately it seems that PC hardware that would support it is becoming
scarce AFAIU.
Most of the patches in clonemode-4 depend on the atomic modesetting series and
can therefore be submitted only after that one.
Alex Deucher
2017-12-15 20:35:53 UTC
Permalink
Post by Pekka Paalanen
On Thu, 14 Dec 2017 10:11:32 -0500
Post by Alex Deucher
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.
Hi Alex,
that's cool. How does userspace take advantage of that, how do you
configure KMS/atomic to make use of that? Is it supported through
legacy KMS (non-atomic) as well?
the driver automatically syncs all heads that it can at modeset time I think.
Post by Pekka Paalanen
Does it involve things like the driver stealing an unused CRTC for each
additional clone?
No, there is logic in the gpu to sync the timing of multiple crtcs.
You just need 1 crtc per display.
Post by Pekka Paalanen
The feature would fit perfectly under the "shared-CRTC clone mode" from
libweston API point of view, even though it's not actually a single
shared CRTC.
Thanks,
pq
Post by Alex Deucher
Alex
Post by Pekka Paalanen
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5
I went through the same testing procedure as with v3, the previous submission.
https://gitlab.collabora.com/pq/weston/commits/clonemode-4
- support for configuring clone mode in weston.ini
- main.c implementation to configure a clode using the new API
- desktop-shell enhancements to avoid redundant panel and background surfaces
in clone mode
- hooking up custom data to weston_output via a new user-side destroy signal
- naming outputs freely
- DRM-backend support for shared-CRTC clone mode
- video mode list merging in the DRM-backend
The shared-CRTC clone mode has been tested to work on an i.MX6 device.
Unfortunately it seems that PC hardware that would support it is becoming
scarce AFAIU.
Most of the patches in clonemode-4 depend on the atomic modesetting series and
can therefore be submitted only after that one.
Pekka Paalanen
2017-12-18 07:38:26 UTC
Permalink
On Fri, 15 Dec 2017 15:35:53 -0500
Post by Alex Deucher
Post by Pekka Paalanen
On Thu, 14 Dec 2017 10:11:32 -0500
Post by Alex Deucher
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.
Hi Alex,
that's cool. How does userspace take advantage of that, how do you
configure KMS/atomic to make use of that? Is it supported through
legacy KMS (non-atomic) as well?
the driver automatically syncs all heads that it can at modeset time I think.
Ok, but we need strong timing guarantees to be able to use it, just
like we have when we have a single CRTC routed to several connectors.
Is there such a guarantee that continues not only on initial modeset
but through all following flips as well?
Post by Alex Deucher
Post by Pekka Paalanen
Does it involve things like the driver stealing an unused CRTC for each
additional clone?
No, there is logic in the gpu to sync the timing of multiple crtcs.
You just need 1 crtc per display.
That is a bit awkward for Weston if userspace needs to assign a CRTC
per connector and then quess whether all the CRTCs will be gen-locked
for good so it needs only a single repaint loop, or not and it needs a
repaint loop per CRTC.

Is there a way to know for sure whether a set of CRTCs will be
gen-locked? Preferably with ATOMIC_TEST_ONLY.


Thanks,
pq
Post by Alex Deucher
Post by Pekka Paalanen
The feature would fit perfectly under the "shared-CRTC clone mode" from
libweston API point of view, even though it's not actually a single
shared CRTC.
Thanks,
pq
Post by Alex Deucher
Alex
Post by Pekka Paalanen
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5
I went through the same testing procedure as with v3, the previous submission.
https://gitlab.collabora.com/pq/weston/commits/clonemode-4
- support for configuring clone mode in weston.ini
- main.c implementation to configure a clode using the new API
- desktop-shell enhancements to avoid redundant panel and background surfaces
in clone mode
- hooking up custom data to weston_output via a new user-side destroy signal
- naming outputs freely
- DRM-backend support for shared-CRTC clone mode
- video mode list merging in the DRM-backend
The shared-CRTC clone mode has been tested to work on an i.MX6 device.
Unfortunately it seems that PC hardware that would support it is becoming
scarce AFAIU.
Most of the patches in clonemode-4 depend on the atomic modesetting series and
can therefore be submitted only after that one.
Alex Deucher
2017-12-20 19:58:14 UTC
Permalink
Post by Pekka Paalanen
On Fri, 15 Dec 2017 15:35:53 -0500
Post by Alex Deucher
Post by Pekka Paalanen
On Thu, 14 Dec 2017 10:11:32 -0500
Post by Alex Deucher
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.
Hi Alex,
that's cool. How does userspace take advantage of that, how do you
configure KMS/atomic to make use of that? Is it supported through
legacy KMS (non-atomic) as well?
the driver automatically syncs all heads that it can at modeset time I think.
Ok, but we need strong timing guarantees to be able to use it, just
like we have when we have a single CRTC routed to several connectors.
Is there such a guarantee that continues not only on initial modeset
but through all following flips as well?
The timing is synchronized so in theory the flips should happen during
the same blanking period. I think atomic may be able to guarantee
that, but while likely I don't think non-atomic can guarantee it.
Post by Pekka Paalanen
Post by Alex Deucher
Post by Pekka Paalanen
Does it involve things like the driver stealing an unused CRTC for each
additional clone?
No, there is logic in the gpu to sync the timing of multiple crtcs.
You just need 1 crtc per display.
That is a bit awkward for Weston if userspace needs to assign a CRTC
per connector and then quess whether all the CRTCs will be gen-locked
for good so it needs only a single repaint loop, or not and it needs a
repaint loop per CRTC.
Is there a way to know for sure whether a set of CRTCs will be
gen-locked? Preferably with ATOMIC_TEST_ONLY.
I don't think so.

Alex
Post by Pekka Paalanen
Thanks,
pq
Post by Alex Deucher
Post by Pekka Paalanen
The feature would fit perfectly under the "shared-CRTC clone mode" from
libweston API point of view, even though it's not actually a single
shared CRTC.
Thanks,
pq
Post by Alex Deucher
Alex
Post by Pekka Paalanen
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5
I went through the same testing procedure as with v3, the previous submission.
https://gitlab.collabora.com/pq/weston/commits/clonemode-4
- support for configuring clone mode in weston.ini
- main.c implementation to configure a clode using the new API
- desktop-shell enhancements to avoid redundant panel and background surfaces
in clone mode
- hooking up custom data to weston_output via a new user-side destroy signal
- naming outputs freely
- DRM-backend support for shared-CRTC clone mode
- video mode list merging in the DRM-backend
The shared-CRTC clone mode has been tested to work on an i.MX6 device.
Unfortunately it seems that PC hardware that would support it is becoming
scarce AFAIU.
Most of the patches in clonemode-4 depend on the atomic modesetting series and
can therefore be submitted only after that one.
Daniel Vetter
2018-01-10 14:35:36 UTC
Permalink
Post by Alex Deucher
Post by Pekka Paalanen
On Fri, 15 Dec 2017 15:35:53 -0500
Post by Alex Deucher
Post by Pekka Paalanen
On Thu, 14 Dec 2017 10:11:32 -0500
Post by Alex Deucher
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.
Hi Alex,
that's cool. How does userspace take advantage of that, how do you
configure KMS/atomic to make use of that? Is it supported through
legacy KMS (non-atomic) as well?
the driver automatically syncs all heads that it can at modeset time I think.
Ok, but we need strong timing guarantees to be able to use it, just
like we have when we have a single CRTC routed to several connectors.
Is there such a guarantee that continues not only on initial modeset
but through all following flips as well?
The timing is synchronized so in theory the flips should happen during
the same blanking period. I think atomic may be able to guarantee
that, but while likely I don't think non-atomic can guarantee it.
We've discussed this a bit internally at intel, and we'll probably have to
export a property where userspace can request that 1 crtc is ganged to
another one. atomic_check in the kernel can then make sure that's either
the case and reject the modeset, and if the modeset passes, userspace
knows that the driver will make sure the flips will stay in sync forever.

Without that generic userspace can indeed not make any assumptions, since
different CRTC might have different clock sources and slowly drift.
Post by Alex Deucher
Post by Pekka Paalanen
Post by Alex Deucher
Post by Pekka Paalanen
Does it involve things like the driver stealing an unused CRTC for each
additional clone?
No, there is logic in the gpu to sync the timing of multiple crtcs.
You just need 1 crtc per display.
That is a bit awkward for Weston if userspace needs to assign a CRTC
per connector and then quess whether all the CRTCs will be gen-locked
for good so it needs only a single repaint loop, or not and it needs a
repaint loop per CRTC.
Is there a way to know for sure whether a set of CRTCs will be
gen-locked? Preferably with ATOMIC_TEST_ONLY.
I don't think so.
yup, doesn't exist yet.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Pekka Paalanen
2018-01-11 11:45:53 UTC
Permalink
On Wed, 10 Jan 2018 15:35:36 +0100
Post by Daniel Vetter
Post by Alex Deucher
Post by Pekka Paalanen
On Fri, 15 Dec 2017 15:35:53 -0500
Post by Alex Deucher
Post by Pekka Paalanen
On Thu, 14 Dec 2017 10:11:32 -0500
Post by Alex Deucher
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
FWIW, at least with the DC modesetting code in amdgpu, we can
synchronize multiple crtcs to the same timing when the monitors all
support the same mode timing or with monitors that have different
timings using variable length blanking periods on freesync capable
monitors.
Hi Alex,
that's cool. How does userspace take advantage of that, how do you
configure KMS/atomic to make use of that? Is it supported through
legacy KMS (non-atomic) as well?
the driver automatically syncs all heads that it can at modeset time I think.
Ok, but we need strong timing guarantees to be able to use it, just
like we have when we have a single CRTC routed to several connectors.
Is there such a guarantee that continues not only on initial modeset
but through all following flips as well?
The timing is synchronized so in theory the flips should happen during
the same blanking period. I think atomic may be able to guarantee
that, but while likely I don't think non-atomic can guarantee it.
We've discussed this a bit internally at intel, and we'll probably have to
export a property where userspace can request that 1 crtc is ganged to
another one. atomic_check in the kernel can then make sure that's either
the case and reject the modeset, and if the modeset passes, userspace
knows that the driver will make sure the flips will stay in sync forever.
Without that generic userspace can indeed not make any assumptions, since
different CRTC might have different clock sources and slowly drift.
Sounds like a nice plan. Weston cannot take advantage of gen-lock
otherwise.


Thanks,
pq
Pekka Paalanen
2017-12-15 10:16:06 UTC
Permalink
On Thu, 14 Dec 2017 13:40:37 +0200
Post by Pekka Paalanen
Hi all,
this is v5 (to match the numbering of my public branches) of the libweston user
facing API and infrastructure for supporting shared-CRTC clone mode.
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://phabricator.freedesktop.org/T7727
- "libweston: properly orphan wl_output resources" is new.
- Removal of wl_output global, when a head is detached from an enabled output.
- Print "Detected a monitor change" only for enabled heads.
https://gitlab.collabora.com/pq/weston/commits/clonemode-user-API-5
I went through the same testing procedure as with v3, the previous submission.
Hi,

thanks to the weston-rdp man page patch on the list, I was finally able
to test the RDP-backend with v5 series and it seems to work fine.


Thanks,
pq
Loading...