Discussion:
[PATCH weston v6 00/73] Head-based output configuration API a.k.a clone mode infrastructure
Pekka Paalanen
2018-02-16 14:56:45 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Hi all,

here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.

The previous submission is here:
https://lists.freedesktop.org/archives/wayland-devel/2017-December/036236.html

Design discussion etc. (sequence diagrams!) can be found here:
https://phabricator.freedesktop.org/T7727
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html

The series is available as a branch at:
https://gitlab.collabora.com/pq/weston/commits/clonemode-6

Highlights since v5:
- DRM-backend migration
- shared-CRTC clone mode implemented in DRM backend
- applied review comments from v5
- don't create a weston_output just to turn it off (DRM)
- cms-colord will print a warning when used with clone mode
- cms-colord vs. clone mode mentioned in weston-drm.man

Unfortunately the testing results are not 100%, you can find my testing
procedure below the diffstat.

The patch series is structured as follows:

- Patches 1-17 introduce most of the new head-based API, and build the
scaffolding that allows migrating the frontends and the backends one
by one.

- Patches 18-24 migrate the frontends one by one. Patch 19 introduces
the simple output configurator which is first used for all backends,
including the DRM-backend.

- Patches 25-30 clean up libweston after the frontend migration.

- Patches 31-38 migrate all the backends to the head-based API. At this
point the DRM-backend migration is basically a fake, though.

- Patch 39 removes weston_output::head with the last bits of the
scaffolding.

- Patches 40-44 enhances libweston core to better support the
DRM-backend's clone mode configuration and improve logging.

- Patches 45-55 implement the head-based API for real in the
DRM-backend, culminating in patch 55 which creates heads for all
connectors.

- Patch 56 removes unused_connectors array which has been replace with
the head list.

- Patches 57-70 finally implement everything needed for shared-CRTC
clone mode in the DRM-backend.

- Patches 71-73 add a new output configrator logic in the frontend to
handle clone mode, supporting a new weston.ini key "same-as".


Do you think we should call the weston.ini key "clone-of" instead, to
leave "same-as" reserved for clone mode where only the desktop area is
the same but the monitors would have different video modes, scaling,
etc.?

There are several imaginable uses for "same-as" variants:
- get me shared-CRTC clone mode or fail
- get me clone mode, preferring shared-CRTC but automatic fallback to
independent CRTCs
- get me clone mode, but I want one monitor with HiDPI and one with
LoDPI

Here I have implemented the automatic fallback version, except it cannot
actually fall back, because we have no API to make two weston_outputs
show the same area, and libweston's damage tracking could not handle it
even if we could.


Pekka Paalanen (73):
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 weston_head_is_enabled()
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-drm: start migration to head-based output API
libweston: remove weston_output::head
libweston: print head names on output enable
libweston: create/find output by name
libweston: support user data on weston_output
libweston: allow attaching heads to enabled outputs
libweston: log head detach on enabled output
compositor-drm: drm_output_find_by_connector from head_list
compositor-drm: use head_find_by_connector in update_unused_outputs
compositor-drm: find disconnects from head_list
compositor-drm: move backlight into drm_head
compositor-drm: move connector fields into drm_head
compositor-drm: allocate CRTC on enable()
compositor-drm: simplify drm_output_find_by_crtc()
compositor-drm: simplify drm_output_find_special_plane()
compositor-drm: get current mode on head discovery
compositor-drm: move mode list to set_mode()
compositor-drm: create heads for all connectors
compositor-drm: remove unused_connectors array
compositor-drm: drm_output_apply_state_legacy heads
compositor-drm: drm_output_apply_state_atomic heads
compositor-drm: drm_set_backlight heads
compositor-drm: unify head status logging
compositor-drm: combine mode list from heads
compositor-drm: backlight control for all heads
compositor-drm: update video mode printing
compositor-drm: introduce drm_head_read_current_setup()
compositor-drm: no need to clear inherited_mode
compositor-drm: rewrite crtc picking for clone mode
compositor-drm: preserve CRTC routing harder
compositor-drm: head detach requires a modeset
compositor-drm: head attach requires a modeset
compositor-drm: allow shared-CRTC cloning
weston: store weston_compositor in wet_compositor
weston: use wet.compositor consistently in main()
weston: support clone mode on DRM-frontend

compositor/cms-colord.c | 45 +-
compositor/main.c | 818 +++++++++++++++++++++----
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-v6.c | 2 +-
libweston/compositor-drm.c | 1128 +++++++++++++++++++++++------------
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 | 1107 +++++++++++++++++++++++++++++++---
libweston/compositor.h | 198 +++++-
libweston/windowed-output-api.h | 23 +-
man/weston-drm.man | 12 +
tests/weston-test.c | 2 +-
20 files changed, 3249 insertions(+), 776 deletions(-)


Testing procedure and results:

All tests were successful except where otherwise mentioned.


headless-backend testing:

$ make -j7 distcheck


x11-backend testing:

$ weston
$ weston --output-count=3
- close x11 windows one by one
$ weston --fullscreen (equally broken before and after)


Wayland-backend testing:

parent: $ weston --output-count=2 --width=700
outputs have different height

tests:

weston --use-pixman
weston --use-pixman --fullscreen
weston --use-pixman --output-count=2
weston --use-pixman --sprawl
- close parent outputs one by one

In the --sprawl case, closing the first output soon results in the
parent compositor:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
467 wl_list_for_each_safe(l, next, &signal->listener_list, link)
(gdb) bt
#0 0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
#1 0x00007ffff7ba8a4c in weston_surface_destroy (surface=0x555555da74a0) at /home/pq/git/weston/libweston/compositor.c:1904
#2 0x00007fffee108196 in fade_out_done_idle_cb (data=0x555555da7d20) at /home/pq/git/weston/desktop-shell/shell.c:2281
#3 0x00007ffff798dcac in wl_event_loop_dispatch_idle (loop=***@entry=0x55555576c250) at src/event-loop.c:600
#4 0x00007ffff798dd64 in wl_event_loop_dispatch (loop=0x55555576c250, timeout=***@entry=-1) at src/event-loop.c:646
#5 0x00007ffff798c4fa in wl_display_run (display=0x55555576c170) at src/wayland-server.c:1260
#6 0x000055555555dd86 in main (argc=1, argv=0x7fffffffdb58) at /home/pq/git/weston/compositor/main.c:2477



parent: $ weston --output-count=2 --width=700 --shell=fullscreen-shell.so

tests:

weston --use-pixman
- close parent outputs one by one


RDP-backend testing:

- using remmina as client, connected twice in a row



fbdev-backend testing:

$ weston -Bfbdev-backend.so


DRM-backend testing:

Two outputs plugged in, third unplugged.
1. start Weston, ensure both outputs work
2. unplug one output, ensure remaining output works
3. re-plug the output, ensure both outputs work
4. quit

One output plugged in, the rest unplugged. The plugged-in output is
configured to be off in weston.ini.
1. start Weston, zero outputs, check via remote login weston-info works
2. plug in an output, check it works
3. unplug the output, check weston-info via remote



Thanks,
pq
Pekka Paalanen
2018-02-16 14:56:46 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.

v6:
- adapt to upstream changes in weston_output_set_transform()

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
compositor/cms-colord.c | 32 +++++------
libweston/compositor-drm.c | 17 +++---
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 | 116 ++++++++++++++++++++++++++++++++++------
libweston/compositor.h | 38 +++++++++++--
9 files changed, 191 insertions(+), 71 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 321ee191..5ce0255b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4951,6 +4951,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;
@@ -4991,26 +4992,26 @@ 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));

drmModeFreeObjectProperties(props);

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);

if (drm_output_init_gamma_size(output) < 0)
goto err_output;

- output->state_cur = drm_output_state_alloc(output, NULL);
+ weston_head_set_physical_size(head, output->connector->mmWidth,
+ output->connector->mmHeight);

- output->base.mm_width = output->connector->mmWidth;
- output->base.mm_height = output->connector->mmHeight;
+ output->state_cur = drm_output_state_alloc(output, NULL);

for (i = 0; i < output->connector->count_modes; i++) {
drm_mode = drm_output_add_mode(output, &output->connector->modes[i]);
diff --git a/libweston/compositor-fbdev.c b/libweston/compositor-fbdev.c
index c63b1fc1..7db95d21 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 9307a36a..d6ab9db9 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 4a4dc696..cfc5b07e 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 c6b92611..8e99a477 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 14faeda0..de19fad8 100644
--- a/libweston/compositor-x11.c
+++ b/libweston/compositor-x11.c
@@ -1065,6 +1065,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. */
@@ -1099,16 +1101,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 aec937bb..95234c87 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4314,6 +4314,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);
@@ -4328,10 +4329,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,
@@ -4364,6 +4365,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
@@ -4480,6 +4560,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;
@@ -4500,11 +4581,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)
@@ -4694,6 +4775,7 @@ weston_output_set_transform(struct weston_output *output,
struct weston_seat *seat;
pixman_region32_t old_region;
int mid_x, mid_y;
+ struct weston_head *head = &output->head;

if (!output->enabled && output->transform == UINT32_MAX) {
output->transform = transform;
@@ -4717,11 +4799,11 @@ weston_output_set_transform(struct weston_output *output,
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)
@@ -4768,6 +4850,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);
@@ -4777,8 +4861,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 ca1acc60..3be5660d 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,
@@ -1931,6 +1944,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
Pekka Paalanen
2018-02-16 14:56:47 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.

v6:
- adapt to upstream changes in weston_output_set_transform()

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 41 +++++++++++++++++++++++++----------------
libweston/compositor.h | 5 +++--
2 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 95234c87..99a56269 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,12 +343,14 @@ 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;
uint32_t tv_sec_hi;
uint32_t tv_sec_lo;
uint32_t tv_nsec;

- 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;

@@ -922,7 +927,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.
*
@@ -931,7 +936,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)
{
@@ -941,7 +946,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;

@@ -981,7 +986,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);
}
@@ -4323,7 +4328,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,
@@ -4577,7 +4582,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,
@@ -4611,6 +4616,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);

@@ -4628,9 +4634,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);

@@ -4702,6 +4709,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);
@@ -4722,9 +4730,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);
}

@@ -4795,7 +4804,7 @@ weston_output_set_transform(struct weston_output *output,
output->dirty = 1;

/* Notify clients of the change for output transform. */
- 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,
@@ -4969,7 +4978,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 3be5660d..c2de761b 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
2018-02-16 14:56:49 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>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
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-v6.c | 2 +-
libweston/compositor.c | 6 +++---
libweston/compositor.h | 4 ++--
tests/weston-test.c | 2 +-
10 files changed, 19 insertions(+), 15 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 ceb45c74..b846e305 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -2969,7 +2969,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;

sh_output = find_shell_output_from_weston_output(shell, surface->output);
@@ -3066,7 +3066,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;

sh_output = find_shell_output_from_weston_output(shell, surface->output);
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 8467dfb8..37720acb 100644
--- a/libweston-desktop/wl-shell.c
+++ b/libweston-desktop/wl-shell.c
@@ -308,7 +308,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-v6.c b/libweston-desktop/xdg-shell-v6.c
index f9902ff0..654af9c6 100644
--- a/libweston-desktop/xdg-shell-v6.c
+++ b/libweston-desktop/xdg-shell-v6.c
@@ -515,7 +515,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 aa636341..50b6f639 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4329,7 +4329,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);
@@ -4363,8 +4363,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 4e8797c7..941d933a 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1989,8 +1989,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 9a2fd286..73409cac 100644
--- a/tests/weston-test.c
+++ b/tests/weston-test.c
@@ -530,7 +530,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
Pekka Paalanen
2018-02-16 14:56:50 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>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 57 +++++++++++++++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 50b6f639..64664f3c 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
Pekka Paalanen
2018-02-16 14:56:48 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>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
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 99a56269..aa636341 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4316,10 +4316,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);
@@ -4329,8 +4329,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,
@@ -4635,9 +4637,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 c2de761b..4e8797c7 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
2018-02-16 14:56:52 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>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 32 ++++++++++++++++++++++++--------
1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index e01c138c..baca2d67 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4375,6 +4375,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.
@@ -4824,7 +4846,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;

@@ -4847,13 +4868,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
2018-02-16 14:56:53 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>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index baca2d67..4647679d 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4502,6 +4502,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
@@ -4523,9 +4527,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
2018-02-16 14:56:56 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.

v6:
- split weston_head_is_enabled() to a new patch

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 43 +++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 7 +++++++
2 files changed, 50 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 55990a9e..ee7e29c8 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4598,6 +4598,48 @@ 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;
+}
+
/* Move other outputs when one is resized so the space remains contiguous. */
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
@@ -5020,6 +5062,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 84222ee0..1612fe95 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 {
@@ -1965,8 +1966,14 @@ 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);
+
void
weston_output_set_scale(struct weston_output *output,
int32_t scale);
--
2.13.6
Pekka Paalanen
2018-02-16 14:56:57 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Enabled is orthogonal from connected. A connected head could be
disabled, or a disconnected head could in the future be enabled.

Compositors quite likely want to check if a head is already enabled
before starting to take it into use.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 21 +++++++++++++++++++++
libweston/compositor.h | 3 +++
2 files changed, 24 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index ee7e29c8..d1cfc109 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4640,6 +4640,27 @@ 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,
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 1612fe95..bad73599 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1974,6 +1974,9 @@ 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
Pekka Paalanen
2018-02-16 14:56:59 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.

v6:
- fix a typo
- add comment in weston_pending_output_coldplug()

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++-
libweston/compositor.h | 8 +++++
2 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index cc71a18e..8153540c 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4439,12 +4439,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
@@ -4458,6 +4493,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
+ * check their statuses 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
@@ -4681,13 +4740,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?
@@ -5348,6 +5417,15 @@ 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);
+
+ /* Execute the heads changed callback manually to ensure it is
+ * processed before any plugins get their start-up idle tasks ran.
+ * This ensures the plugins see all the initial outputs.
+ */
+ if (compositor->heads_changed_source) {
+ wl_event_source_remove(compositor->heads_changed_source);
+ weston_compositor_call_heads_changed(compositor);
+ }
}

/** Uninitialize an output
@@ -6059,6 +6137,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 7c95b454..4fb9d88f 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -882,6 +882,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;

@@ -977,6 +979,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 {
@@ -1986,6 +1990,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
Pekka Paalanen
2018-02-16 14:56:54 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.

The warning is printed in get_output_id(), because if heads are added or
removed while the output is enabled, the id could change.

v6:
- add weston_output_get_first_head(), at first use
- add warning message for nr. heads > 1

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

diff --git a/compositor/cms-colord.c b/compositor/cms-colord.c
index f421773b..b68e4921 100644
--- a/compositor/cms-colord.c
+++ b/compositor/cms-colord.c
@@ -102,10 +102,20 @@ 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?
+ * This is potentially unstable, if head configuration is changed
+ * while the output is enabled. */
+ head = weston_output_get_first_head(o);
+
+ if (wl_list_length(&o->head_list) > 1) {
+ weston_log("colord: WARNING: multiple heads are not supported (output %s).\n",
+ o->name);
+ }
+
/* 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 +241,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 +249,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);
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 4647679d..a73af222 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5243,6 +5243,24 @@ weston_output_release(struct weston_output *output)
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 8a4ddd74..78b6e2f6 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1994,6 +1994,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
Pekka Paalanen
2018-02-16 14:57:02 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.

v6:
- change str_null_neq() to str_null_eq()
- rename weston_head_condition_device_changed()
- move the condition from weston_head_set_device_changed() to the
callers

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index d3c0d573..02d92faf 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4715,6 +4715,28 @@ weston_head_release(struct weston_head *head)
wl_list_remove(&head->compositor_link);
}

+static void
+weston_head_set_device_changed(struct weston_head *head)
+{
+ head->device_changed = true;
+
+ if (head->compositor)
+ weston_compositor_schedule_heads_changed(head->compositor);
+}
+
+/** String equal comparison with NULLs being equal */
+static bool
+str_null_eq(const char *a, const char *b)
+{
+ if (!a && !b)
+ return true;
+
+ if (!!a != !!b)
+ return false;
+
+ return strcmp(a, b) == 0;
+}
+
/** Store monitor make, model and serial number
*
* \param head The head to modify.
@@ -4725,6 +4747,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
*/
@@ -4734,6 +4758,11 @@ weston_head_set_monitor_strings(struct weston_head *head,
const char *model,
const char *serialno)
{
+ if (str_null_eq(head->make, make) &&
+ str_null_eq(head->model, model) &&
+ str_null_eq(head->serial_number, serialno))
+ return;
+
free(head->make);
free(head->model);
free(head->serial_number);
@@ -4741,6 +4770,8 @@ weston_head_set_monitor_strings(struct weston_head *head,
head->make = make ? strdup(make) : NULL;
head->model = model ? strdup(model) : NULL;
head->serial_number = serialno ? strdup(serialno) : NULL;
+
+ weston_head_set_device_changed(head);
}

/** Store physical image size
@@ -4749,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
*/
@@ -4756,8 +4789,14 @@ WL_EXPORT void
weston_head_set_physical_size(struct weston_head *head,
int32_t mm_width, int32_t mm_height)
{
+ if (head->mm_width == mm_width &&
+ head->mm_height == mm_height)
+ return;
+
head->mm_width = mm_width;
head->mm_height = mm_height;
+
+ weston_head_set_device_changed(head);
}

/** Store monitor sub-pixel layout
@@ -4771,6 +4810,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
*/
@@ -4778,7 +4819,12 @@ WL_EXPORT void
weston_head_set_subpixel(struct weston_head *head,
enum wl_output_subpixel sp)
{
+ if (head->subpixel == sp)
+ return;
+
head->subpixel = sp;
+
+ weston_head_set_device_changed(head);
}

/** Mark the monitor as internal
@@ -4813,7 +4859,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
@@ -4827,8 +4873,7 @@ weston_head_set_connection_status(struct weston_head *head, bool connected)

head->connected = connected;

- if (head->compositor)
- weston_compositor_schedule_heads_changed(head->compositor);
+ weston_head_set_device_changed(head);
}

/** Is the head currently connected?
@@ -4872,6 +4917,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 4a15502b..4b866932 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 */
@@ -2040,6 +2041,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
Pekka Paalanen
2018-02-16 14:57:03 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 18810f28..979da2a0 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1006,8 +1006,6 @@ wet_configure_windowed_output_from_config(struct weston_output *output,
return -1;
}

- weston_output_enable(output);
-
return 0;
}

@@ -1151,6 +1149,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
@@ -1369,6 +1369,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
@@ -1485,6 +1487,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
2018-02-16 14:56:58 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>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 8 ++++++
2 files changed, 83 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index d1cfc109..cc71a18e 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4433,11 +4433,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.
@@ -4512,6 +4580,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
@@ -5084,6 +5154,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
@@ -5707,6 +5778,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);
@@ -5982,6 +6054,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 bad73599..7c95b454 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 */

@@ -914,6 +917,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 */
@@ -1977,6 +1981,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
2018-02-16 14:56:51 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.

v6:
- adapt to upstream changes in weston_output_set_transform()
- use wl_list_for_each_safe in weston_output_release()
- removed weston_output_get_first_head() as it's not needed yet

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
v5 Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 228 +++++++++++++++++++++++++++++++++++++------------
libweston/compositor.h | 4 +-
2 files changed, 176 insertions(+), 56 deletions(-)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 64664f3c..e01c138c 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);
@@ -364,12 +363,13 @@ weston_presentation_feedback_present(
uint32_t tv_sec_lo;
uint32_t tv_nsec;

- 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);
+ }
}

timespec_to_proto(ts, &tv_sec_hi, &tv_sec_lo, &tv_nsec);
@@ -989,6 +989,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)
@@ -1001,9 +1002,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);
+ }
}
}

@@ -4387,6 +4390,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.
@@ -4582,8 +4677,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;
@@ -4599,19 +4695,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);
+ }
}
}

@@ -4651,11 +4750,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);

@@ -4748,11 +4847,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);
@@ -4802,7 +4902,8 @@ weston_output_set_transform(struct weston_output *output,
struct weston_seat *seat;
pixman_region32_t old_region;
int mid_x, mid_y;
- struct weston_head *head = &output->head;
+ struct weston_head *head;
+ int ver;

if (!output->enabled && output->transform == UINT32_MAX) {
output->transform = transform;
@@ -4822,19 +4923,22 @@ weston_output_set_transform(struct weston_output *output,
output->dirty = 1;

/* Notify clients of the change for output transform. */
- 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);
+ }
}

/* we must ensure that pointers are inside output, otherwise they disappear */
@@ -4877,19 +4981,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;
@@ -4963,6 +5067,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",
@@ -4996,9 +5101,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)
@@ -5090,6 +5200,8 @@ weston_pending_output_coldplug(struct weston_compositor *compositor)
WL_EXPORT void
weston_output_release(struct weston_output *output)
{
+ struct weston_head *head, *tmp;
+
output->destroying = 1;

if (output->enabled)
@@ -5098,6 +5210,12 @@ weston_output_release(struct weston_output *output)
pixman_region32_fini(&output->region);
pixman_region32_fini(&output->previous_damage);
wl_list_remove(&output->link);
+
+ wl_list_for_each_safe(head, tmp, &output->head_list, output_link)
+ weston_head_detach(head);
+
+ weston_head_release(&output->head);
+
free(output->name);
}

diff --git a/libweston/compositor.h b/libweston/compositor.h
index 941d933a..8a4ddd74 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,
--
2.13.6
Pekka Paalanen
2018-02-16 14:56:55 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>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
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 a73af222..55990a9e 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4415,14 +4415,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
@@ -4431,6 +4435,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
@@ -4506,6 +4511,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
@@ -5013,7 +5019,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 78b6e2f6..84222ee0 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
2018-02-16 14:57:00 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>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
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 8153540c..940c14dd 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4425,7 +4425,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
@@ -4484,7 +4484,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)
{
@@ -4565,6 +4565,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.
@@ -4584,7 +4630,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)
{
@@ -4594,9 +4640,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);
@@ -4610,14 +4660,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
@@ -4630,7 +4699,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);
@@ -4800,6 +4869,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,
@@ -5222,8 +5316,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
@@ -5462,6 +5559,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 4fb9d88f..cb879bcf 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 {
@@ -877,6 +906,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;
@@ -1960,6 +2004,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,
@@ -1985,6 +2039,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);
@@ -1993,6 +2056,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
Pekka Paalanen
2018-02-16 14:57:01 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>
Reviewed-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
libweston/compositor.h | 9 +++++++++
2 files changed, 54 insertions(+)

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 940c14dd..d3c0d573 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4434,6 +4434,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);
@@ -4702,6 +4703,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);
@@ -4894,6 +4897,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 cb879bcf..4a15502b 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 */
@@ -2048,6 +2049,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
Pekka Paalanen
2018-02-16 14:57:04 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 979da2a0..5a2cec97 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -69,11 +69,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;
@@ -1009,6 +1017,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)
@@ -1136,10 +1315,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,
@@ -1147,10 +1325,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
@@ -1188,6 +1363,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);
@@ -1195,8 +1372,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);

@@ -1675,7 +1850,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;

@@ -1790,6 +1965,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
2018-02-16 14:57:07 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 873f7a3a..3905c3ba 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1481,10 +1481,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;

@@ -1493,7 +1492,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
@@ -1517,6 +1516,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);
@@ -1524,8 +1525,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
Ray, Ian (GE Healthcare)
2018-03-23 13:25:52 UTC
Permalink
Post by Pekka Paalanen
Migrate the fbdev frontend to use the new head-based output
configuration API: listen for heads_changed, and process all heads.
---
compositor/main.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/compositor/main.c b/compositor/main.c
index 873f7a3a..3905c3ba 100644
--- a/compositor/main.c
+++ b/compositor/main.c
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;
@@ -1493,7 +1492,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
@@ -1517,6 +1516,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);
@@ -1524,8 +1525,6 @@ load_fbdev_backend(struct weston_compositor *c,
if (ret < 0)
goto out;
Nit: goto out is now redundant.
Post by Pekka Paalanen
- wet_set_pending_output_handler(c, fbdev_backend_output_configure);
-
free(config.device);
return ret;
--
2.13.6
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-02-16 14:57:09 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 02f20d13..12d54083 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -78,7 +78,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;
@@ -360,16 +359,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)
{
@@ -1122,6 +1111,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));
@@ -1212,10 +1205,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;
@@ -1230,7 +1222,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);
@@ -1239,7 +1231,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) {
@@ -1251,7 +1243,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);

@@ -1269,7 +1261,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
@@ -1304,11 +1296,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
2018-02-16 14:57:11 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 | 21 +++++----------------
libweston/compositor.h | 3 +--
3 files changed, 7 insertions(+), 19 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index 61587870..c9554625 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1939,7 +1939,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 02d92faf..2a2ec845 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5467,9 +5467,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
@@ -5484,7 +5481,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.
@@ -5631,22 +5627,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);
-
- /* Execute the heads changed callback manually to ensure it is
- * processed before any plugins get their start-up idle tasks ran.
- * This ensures the plugins see all the initial outputs.
- */
if (compositor->heads_changed_source) {
wl_event_source_remove(compositor->heads_changed_source);
weston_compositor_call_heads_changed(compositor);
@@ -6115,7 +6105,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 4b866932..16af3cfb 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -951,7 +951,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;
@@ -2111,7 +2110,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
2018-02-16 14:57:10 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 12d54083..61587870 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1372,7 +1372,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;
}

@@ -1600,7 +1600,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;
}
@@ -1616,7 +1616,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;
}
@@ -1732,7 +1732,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;
}
@@ -1745,7 +1745,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
Ray, Ian (GE Healthcare)
2018-03-23 13:36:53 UTC
Permalink
Post by Pekka Paalanen
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.
Nit: it’s not an API break, it’s a rename.
Post by Pekka Paalanen
---
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 12d54083..61587870 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1372,7 +1372,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;
}
@@ -1600,7 +1600,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;
}
@@ -1616,7 +1616,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;
}
@@ -1732,7 +1732,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;
}
@@ -1745,7 +1745,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
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Ray, Ian (GE Healthcare)
2018-03-23 13:42:30 UTC
Permalink
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
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.
Nit: it’s not an API break, it’s a rename.
Apologies, I misread. Strictly it is indeed an ‘API break’
though I’d call it a rename ;)
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
---
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 12d54083..61587870 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1372,7 +1372,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;
}
@@ -1600,7 +1600,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;
}
@@ -1616,7 +1616,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;
}
@@ -1732,7 +1732,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;
}
@@ -1745,7 +1745,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
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-02-16 14:57:05 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 5a2cec97..5d5ff248 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1531,10 +1531,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,
@@ -1542,10 +1541,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
@@ -1581,6 +1577,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);
@@ -1588,8 +1586,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
2018-02-16 14:57:06 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 5d5ff248..873f7a3a 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1636,19 +1636,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,
@@ -1656,10 +1646,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
@@ -1726,13 +1713,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
2018-02-16 14:57:13 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 59b7fe24..e69d8f35 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5521,6 +5521,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) {
@@ -5535,6 +5536,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
2018-02-16 14:57:12 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 2a2ec845..59b7fe24 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5486,7 +5486,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.
@@ -5522,7 +5522,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",
@@ -5530,6 +5529,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);

@@ -5558,12 +5563,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
2018-02-16 14:57:15 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 4b215ec2..311ea96e 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -2583,6 +2583,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);
}

@@ -2707,7 +2708,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);
}

@@ -5673,6 +5676,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 16af3cfb..31e94c61 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
2018-02-16 14:57:16 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 d6ab9db9..6cb4f206 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,33 +243,71 @@ 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_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 *
@@ -278,6 +328,7 @@ headless_backend_create(struct weston_compositor *compositor,
goto err_free;

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

b->use_pixman = config->use_pixman;
if (b->use_pixman) {
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:17 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 cfc5b07e..3f032230 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,33 +588,62 @@ 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_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]);
@@ -1298,6 +1339,7 @@ rdp_backend_create(struct weston_compositor *compositor,

b->compositor = compositor;
b->base.destroy = rdp_destroy;
+ 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;

@@ -1319,7 +1361,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
Ray, Ian (GE Healthcare)
2018-03-24 09:12:08 UTC
Permalink
Post by Pekka Paalanen
Follow the starndard patttern as the other backends, headless and x11 in
Nit: s/starndard/standard

(And, at risk of being overly pedantic, the X11 change does not come
until patch 35.)
Post by Pekka Paalanen
particular, to stop relying on the implicit weston_output::head.
---
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 cfc5b07e..3f032230 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,33 +588,62 @@ 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_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]);
@@ -1298,6 +1339,7 @@ rdp_backend_create(struct weston_compositor *compositor,
b->compositor = compositor;
b->base.destroy = rdp_destroy;
+ 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;
@@ -1319,7 +1361,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
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-02-16 14:57:14 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 e69d8f35..4b215ec2 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5536,6 +5536,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
2018-02-16 14:57:08 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 3905c3ba..02f20d13 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1387,10 +1387,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);
@@ -1401,7 +1400,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)
@@ -1416,10 +1415,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
@@ -1464,14 +1463,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
Ray, Ian (GE Healthcare)
2018-03-23 13:27:07 UTC
Permalink
Post by Pekka Paalanen
Migrate the RDP frontend to use the new head-based output configuration
API: listen for heads_changed, and process all heads.
---
compositor/main.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/compositor/main.c b/compositor/main.c
index 3905c3ba..02f20d13 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1387,10 +1387,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);
@@ -1401,7 +1400,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)
@@ -1416,10 +1415,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
@@ -1464,14 +1463,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;
Nit: goto out is now redundant.
Post by Pekka Paalanen
- wet_set_pending_output_handler(c, rdp_backend_output_configure);
-
free(config.bind_address);
free(config.rdp_key);
--
2.13.6
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-02-16 14:57:18 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 7db95d21..af338625 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
2018-02-16 14:57:20 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 de19fad8..78b0d7c6 100644
--- a/libweston/compositor-x11.c
+++ b/libweston/compositor-x11.c
@@ -116,6 +116,10 @@ struct x11_backend {
} atom;
};

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

@@ -142,6 +146,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)
{
@@ -1065,7 +1075,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;

@@ -1087,6 +1097,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;

@@ -1104,17 +1121,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;

@@ -1122,22 +1133,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)
{
@@ -1745,12 +1780,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);
}
@@ -1774,7 +1813,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 *
@@ -1833,6 +1872,7 @@ x11_backend_create(struct weston_compositor *compositor,
weston_log("Using %s renderer\n", config->use_pixman ? "pixman" : "gl");

b->base.destroy = x11_destroy;
+ 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
2018-02-16 14:57:19 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 af338625..a4ac3039 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);

@@ -732,6 +808,7 @@ fbdev_backend_create(struct weston_compositor *compositor,
}

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

backend->prev_state = WESTON_COMPOSITOR_ACTIVE;

@@ -740,7 +817,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
Ray, Ian (GE Healthcare)
2018-03-24 09:25:52 UTC
Permalink
Post by Pekka Paalanen
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.
---
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 af338625..a4ac3039 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)
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);
+ 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;
- 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”);
Nit: other migrations pass `name' here.
Post by Pekka Paalanen
+
+ 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);
@@ -732,6 +808,7 @@ fbdev_backend_create(struct weston_compositor *compositor,
}
backend->base.destroy = fbdev_backend_destroy;
+ backend->base.create_output = fbdev_output_create;
backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
@@ -740,7 +817,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
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-02-16 14:57:22 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 cf4a5f3f..455d94a0 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);
@@ -2467,11 +2579,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);

@@ -2626,6 +2742,7 @@ wayland_backend_create(struct weston_compositor *compositor,
}

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

loop = wl_display_get_event_loop(compositor->wl_display);

@@ -2670,7 +2787,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
@@ -2707,14 +2824,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
Ray, Ian (GE Healthcare)
2018-03-24 10:13:48 UTC
Permalink
Post by Pekka Paalanen
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".
---
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 cf4a5f3f..455d94a0 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)
{
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)
Nit: head unused. (GCC_CFLAGS contains `-Wno-unused-parameter' so
presumably I should not report similar occurrences?)
Post by Pekka Paalanen
+{
+ 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;
-
- 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;
wayland_backend_destroy_output_surface(output);
- 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);
@@ -2467,11 +2579,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);
@@ -2626,6 +2742,7 @@ wayland_backend_create(struct weston_compositor *compositor,
}
b->base.destroy = wayland_destroy;
+ b->base.create_output = wayland_output_create;
loop = wl_display_get_event_loop(compositor->wl_display);
@@ -2670,7 +2787,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
@@ -2707,14 +2824,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
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-03-26 10:57:53 UTC
Permalink
On Sat, 24 Mar 2018 10:13:48 +0000
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
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".
---
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 cf4a5f3f..455d94a0 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;
+};
+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)
Nit: head unused. (GCC_CFLAGS contains `-Wno-unused-parameter' so
presumably I should not report similar occurrences?)
Hi Ian,

yes, it's not a mistake. There are lots of code in Weston in general
where functions that need to follow API do not actually use all their
arguments.

In this specific case, wayland-backend can only ever have a single head
per output, so if any head is removed, it was sure to be the only head
on the output which is then left with none. I'm not sure I could put
even an assert here to assure the head argument is correct even if
unnecessary.
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
+{
+ 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);
+}
Thanks,
pq
Pekka Paalanen
2018-02-16 14:57:23 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Hook up the libweston facing head-based output API by introducing struct
drm_head, but leave it as a fake so that members can be migrated in
pieces in follow-up patches.

The DRM backend continues to create an output for each connected
connector only, and during output creation it also creates a drm_head
for it. This allows it to pretend it supports the head-based output API
as long as there is only one head per output ever attached.

create_output callback is fake, it will only look up the existing
drm_output by the head name.

Clones are not yet supported, hence max is defined to 1.

This unfortunately introduces some temporary code that will be revomed
later, but seems to be necessary to avoid a single big patch.

v6:
- add missing drm_head_destroy() call

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5ce0255b..58aae1ac 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -83,6 +83,8 @@
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif

+#define MAX_CLONED_CONNECTORS 1
+
/**
* Represents the values of an enum-type KMS property
*/
@@ -401,6 +403,13 @@ struct drm_plane {
uint32_t formats[];
};

+struct drm_head {
+ struct weston_head base;
+ struct drm_backend *backend;
+
+ struct drm_output *output; /* XXX: temporary */
+};
+
struct drm_output {
struct weston_output base;
drmModeConnector *connector;
@@ -476,6 +485,12 @@ wl_array_remove_uint32(struct wl_array *array, uint32_t elm)
}
}

+static inline struct drm_head *
+to_drm_head(struct weston_head *base)
+{
+ return container_of(base, struct drm_head, base);
+}
+
static inline struct drm_output *
to_drm_output(struct weston_output *base)
{
@@ -4412,6 +4427,16 @@ setup_output_seat_constraint(struct drm_backend *b,
}

static int
+drm_output_attach_head(struct weston_output *output_base,
+ struct weston_head *head_base)
+{
+ if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS)
+ return -1;
+
+ return 0;
+}
+
+static int
parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
{
int ret = 0;
@@ -4829,10 +4854,14 @@ drm_output_deinit(struct weston_output *base)
}

static void
+drm_head_destroy(struct drm_head *head);
+
+static void
drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
+ struct weston_head *head = weston_output_get_first_head(base);

if (output->page_flip_pending || output->vblank_pending ||
output->atomic_complete_pending) {
@@ -4863,6 +4892,10 @@ drm_output_destroy(struct weston_output *base)
drm_output_state_free(output->state_cur);

free(output);
+
+ /* XXX: temporary */
+ if (head)
+ drm_head_destroy(to_drm_head(head));
}

static int
@@ -4886,6 +4919,19 @@ drm_output_disable(struct weston_output *base)
return 0;
}

+static struct weston_output *
+drm_output_create(struct weston_compositor *compositor, const char *name)
+{
+ struct drm_head *head;
+
+ /* XXX: Temporary until we can have heads without an output */
+ wl_list_for_each(head, &compositor->head_list, base.compositor_link)
+ if (strcmp(name, head->base.name) == 0)
+ return &head->output->base;
+
+ return NULL;
+}
+
/**
* Update the list of unused connectors and CRTCs
*
@@ -4932,6 +4978,71 @@ drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources)
}

/**
+ * Create a Weston head for a connector
+ *
+ * Given a DRM connector, create a matching drm_head structure and add it
+ * to Weston's head list.
+ *
+ * @param b Weston backend structure
+ * @param connector_id DRM connector ID for the head
+ * @param drm_device udev device pointer
+ * @returns The new head, or NULL on failure.
+ */
+static struct drm_head *
+drm_head_create(struct drm_backend *backend, uint32_t connector_id,
+ struct udev_device *drm_device)
+{
+ struct drm_head *head;
+ drmModeConnector *connector;
+ char *name;
+
+ head = zalloc(sizeof *head);
+ if (!head)
+ return NULL;
+
+ connector = drmModeGetConnector(backend->drm.fd, connector_id);
+ if (!connector)
+ goto err_alloc;
+
+ name = make_connector_name(connector);
+ if (!name)
+ goto err_alloc;
+
+ weston_head_init(&head->base, name);
+ free(name);
+
+ head->backend = backend;
+
+ /* Unknown connection status is assumed disconnected. */
+ weston_head_set_connection_status(&head->base,
+ connector->connection == DRM_MODE_CONNECTED);
+
+ weston_compositor_add_head(backend->compositor, &head->base);
+ drmModeFreeConnector(connector);
+
+ weston_log("DRM: found head '%s', connector %d %s.\n",
+ head->base.name, connector_id,
+ head->base.connected ? "connected" : "disconnected");
+
+ return head;
+
+err_alloc:
+ if (connector)
+ drmModeFreeConnector(connector);
+
+ free(head);
+
+ return NULL;
+}
+
+static void
+drm_head_destroy(struct drm_head *head)
+{
+ weston_head_release(&head->base);
+ free(head);
+}
+
+/**
* Create a Weston output structure
*
* Given a DRM connector, create a matching drm_output structure and add it
@@ -4951,7 +5062,7 @@ create_output_for_connector(struct drm_backend *b,
struct udev_device *drm_device)
{
struct drm_output *output;
- struct weston_head *head;
+ struct drm_head *head;
drmModeObjectPropertiesPtr props;
struct drm_mode *drm_mode;
char *name;
@@ -4974,9 +5085,16 @@ create_output_for_connector(struct drm_backend *b,
weston_output_init(&output->base, b->compositor, name);
free(name);

+ /* XXX: temporary */
+ head = drm_head_create(b, connector->connector_id, drm_device);
+ if (!head)
+ abort();
+ head->output = output;
+
output->base.enable = drm_output_enable;
output->base.destroy = drm_output_destroy;
output->base.disable = drm_output_disable;
+ output->base.attach_head = drm_output_attach_head;

output->destroy_pending = 0;
output->disable_pending = 0;
@@ -4992,23 +5110,22 @@ 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);
- weston_head_set_monitor_strings(head, make, model, serial_number);
- weston_head_set_subpixel(head,
+ weston_head_set_monitor_strings(&head->base, make, model, serial_number);
+ weston_head_set_subpixel(&head->base,
drm_subpixel_to_wayland(output->connector->subpixel));

drmModeFreeObjectProperties(props);

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

if (drm_output_init_gamma_size(output) < 0)
goto err_output;

- weston_head_set_physical_size(head, output->connector->mmWidth,
+ weston_head_set_physical_size(&head->base, output->connector->mmWidth,
output->connector->mmHeight);

output->state_cur = drm_output_state_alloc(output, NULL);
@@ -5026,6 +5143,7 @@ create_output_for_connector(struct drm_backend *b,
return 0;

err_output:
+ drm_head_destroy(head);
drm_output_destroy(&output->base);
return -1;
/* no fallthrough! */
@@ -5207,6 +5325,7 @@ static void
drm_destroy(struct weston_compositor *ec)
{
struct drm_backend *b = to_drm_backend(ec);
+ struct weston_head *base, *next;

udev_input_destroy(&b->input);

@@ -5219,6 +5338,9 @@ drm_destroy(struct weston_compositor *ec)

weston_compositor_shutdown(ec);

+ wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+ drm_head_destroy(to_drm_head(base));
+
if (b->gbm)
gbm_device_destroy(b->gbm);

@@ -5736,6 +5858,7 @@ drm_backend_create(struct weston_compositor *compositor,
b->base.repaint_begin = drm_repaint_begin;
b->base.repaint_flush = drm_repaint_flush;
b->base.repaint_cancel = drm_repaint_cancel;
+ b->base.create_output = drm_output_create;

weston_setup_vt_switch_bindings(compositor);
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:25 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

This will be interesting to see when testing clone mode.

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 9a8b90f5..c41c7233 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5479,6 +5479,32 @@ weston_compositor_add_pending_output(struct weston_output *output,
wl_list_insert(compositor->pending_output_list.prev, &output->link);
}

+/** Create a string with the attached heads' names.
+ *
+ * The string must be free()'d.
+ */
+static char *
+weston_output_create_heads_string(struct weston_output *output)
+{
+ FILE *fp;
+ char *str = NULL;
+ size_t size = 0;
+ struct weston_head *head;
+ const char *sep = "";
+
+ fp = open_memstream(&str, &size);
+ if (!fp)
+ return NULL;
+
+ wl_list_for_each(head, &output->head_list, output_link) {
+ fprintf(fp, "%s%s", sep, head->name);
+ sep = ", ";
+ }
+ fclose(fp);
+
+ return str;
+}
+
/** 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
@@ -5518,6 +5544,7 @@ weston_output_enable(struct weston_output *output)
struct weston_compositor *c = output->compositor;
struct weston_output *iterator;
struct weston_head *head;
+ char *head_names;
int x = 0, y = 0;

if (output->enabled) {
@@ -5582,6 +5609,11 @@ weston_output_enable(struct weston_output *output)

weston_compositor_add_output(output->compositor, output);

+ head_names = weston_output_create_heads_string(output);
+ weston_log("Output '%s' enabled with head(s) %s\n",
+ output->name, head_names);
+ free(head_names);
+
return 0;
}
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:26 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

To let users pick an arbitrary name for an output, to be used as e.g. a
configuration key, add API to create an output with a given name. For
the same configuration purpose, add a search function as well.

For the search function to be predictable, forbid creating multiple
outputs with the same name. Previously, creating multiple outputs with
the same name would have needed detatching to create outputs from the
same head, now that is forbidden.

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index c41c7233..3bc7a352 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5717,6 +5717,59 @@ weston_output_release(struct weston_output *output)
free(output->name);
}

+/** Find an output by its given name
+ *
+ * \param compositor The compositor to search in.
+ * \param name The output name to search for.
+ * \return An existing output with the given name, or NULL if not found.
+ *
+ * \memberof weston_compositor
+ */
+WL_EXPORT struct weston_output *
+weston_compositor_find_output_by_name(struct weston_compositor *compositor,
+ const char *name)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ if (strcmp(output->name, name) == 0)
+ return output;
+
+ wl_list_for_each(output, &compositor->pending_output_list, link)
+ if (strcmp(output->name, name) == 0)
+ return output;
+
+ return NULL;
+}
+
+/** Create a named output
+ *
+ * \param compositor The compositor.
+ * \param name The name for the output.
+ * \return A new \c weston_output, or NULL on failure.
+ *
+ * This creates a new weston_output that starts with no heads attached.
+ *
+ * An output must be configured and it must have at least one head before
+ * it can be enabled.
+ *
+ * \memberof weston_compositor
+ */
+WL_EXPORT struct weston_output *
+weston_compositor_create_output(struct weston_compositor *compositor,
+ const char *name)
+{
+ assert(compositor->backend->create_output);
+
+ if (weston_compositor_find_output_by_name(compositor, name)) {
+ weston_log("Warning: attempted to create an output with a "
+ "duplicate name '%s'.\n", name);
+ return NULL;
+ }
+
+ return compositor->backend->create_output(compositor, name);
+}
+
/** Create an output for an unused head
*
* \param compositor The compositor.
@@ -5737,8 +5790,7 @@ weston_compositor_create_output_with_head(struct weston_compositor *compositor,
{
struct weston_output *output;

- assert(compositor->backend->create_output);
- output = compositor->backend->create_output(compositor, head->name);
+ output = weston_compositor_create_output(compositor, head->name);
if (!output)
return NULL;

diff --git a/libweston/compositor.h b/libweston/compositor.h
index b67f03fa..a7a5c56a 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -2072,6 +2072,14 @@ weston_compositor_set_heads_changed_cb(struct weston_compositor *compositor,
weston_heads_changed_func_t cb);

struct weston_output *
+weston_compositor_find_output_by_name(struct weston_compositor *compositor,
+ const char *name);
+
+struct weston_output *
+weston_compositor_create_output(struct weston_compositor *compositor,
+ const char *name);
+
+struct weston_output *
weston_compositor_create_output_with_head(struct weston_compositor *compositor,
struct weston_head *head);
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:27 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Support attaching custom data to a weston_output by the traditional
destroy listener / wl_signal_get approach.

Needs a new destroy signal, because user data lifetime should be the
lifetime of the weston_output regradless of its enabled status. The old
destroy signal is for output consumers that only care about enabled
outputs in the system and gets emitted on disable, not on destroy.

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 3bc7a352..b302f8d5 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5442,6 +5442,7 @@ weston_output_init(struct weston_output *output,
output->destroying = 0;
output->name = strdup(name);
wl_list_init(&output->link);
+ wl_signal_init(&output->user_destroy_signal);
output->enabled = false;

wl_list_init(&output->head_list);
@@ -5682,6 +5683,49 @@ weston_compositor_flush_heads_changed(struct weston_compositor *compositor)
}
}

+/** Add destroy callback for an output
+ *
+ * \param output The output to watch.
+ * \param listener The listener to add. The \c notify member must be set.
+ *
+ * The listener callback will be called when user destroys an output. This
+ * may be delayed by a backend in some cases. The main purpose of the
+ * listener is to allow hooking up custom data to the output. The custom data
+ * can be fetched via weston_output_get_destroy_listener() followed by
+ * container_of().
+ *
+ * The \c data argument to the notify callback is the weston_output being
+ * destroyed.
+ *
+ * @note This is for the final destruction of an output, not when it gets
+ * disabled. If you want to keep track of enabled outputs, this is not it.
+ */
+WL_EXPORT void
+weston_output_add_destroy_listener(struct weston_output *output,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&output->user_destroy_signal, listener);
+}
+
+/** Look up destroy listener for an output
+ *
+ * \param output The output 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() weston_output_add_destroy_listener()
+ */
+WL_EXPORT struct wl_listener *
+weston_output_get_destroy_listener(struct weston_output *output,
+ wl_notify_func_t notify)
+{
+ return wl_signal_get(&output->user_destroy_signal, notify);
+}
+
/** Uninitialize an output
*
* Removes the output from the list of enabled outputs if necessary, but
@@ -5701,6 +5745,8 @@ weston_output_release(struct weston_output *output)

output->destroying = 1;

+ wl_signal_emit(&output->user_destroy_signal, output);
+
if (output->idle_repaint_source)
wl_event_source_remove(output->idle_repaint_source);

diff --git a/libweston/compositor.h b/libweston/compositor.h
index a7a5c56a..ff5c004c 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -180,6 +180,9 @@ struct weston_output {
uint32_t id;
char *name;

+ /** Matches the lifetime from the user perspective */
+ struct wl_signal user_destroy_signal;
+
void *renderer_state;

struct wl_list link;
@@ -220,7 +223,7 @@ struct weston_output {
struct weston_output_zoom zoom;
int dirty;
struct wl_signal frame_signal;
- struct wl_signal destroy_signal;
+ struct wl_signal destroy_signal; /**< sent when disabled */
int move_x, move_y;
struct timespec frame_time; /* presentation timestamp */
uint64_t msc; /* media stream counter */
@@ -1805,6 +1808,12 @@ void
weston_output_move(struct weston_output *output, int x, int y);

void
+weston_output_add_destroy_listener(struct weston_output *output,
+ struct wl_listener *listener);
+struct wl_listener *
+weston_output_get_destroy_listener(struct weston_output *output,
+ wl_notify_func_t notify);
+void
weston_output_release(struct weston_output *output);
void
weston_output_transform_coordinate(struct weston_output *output,
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:24 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Remove the scaffolding that allowed backends to be converted one by one
to the head-based API. Nothing is using these members anymore.

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 311ea96e..9a8b90f5 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5446,13 +5446,6 @@ weston_output_init(struct weston_output *output,

wl_list_init(&output->head_list);

- weston_head_init(&output->head, name);
- 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
*/
@@ -5689,8 +5682,6 @@ weston_output_release(struct weston_output *output)
wl_list_for_each_safe(head, tmp, &output->head_list, output_link)
weston_head_detach(head);

- weston_head_release(&output->head);
-
free(output->name);
}

@@ -5714,22 +5705,13 @@ weston_compositor_create_output_with_head(struct weston_compositor *compositor,
{
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);
- }
-
+ 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);
-
+ weston_output_destroy(output);
return NULL;
}

@@ -5751,18 +5733,6 @@ weston_compositor_create_output_with_head(struct weston_compositor *compositor,
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);
}

diff --git a/libweston/compositor.h b/libweston/compositor.h
index 31e94c61..b67f03fa 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -174,8 +174,6 @@ 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 {
@@ -240,7 +238,6 @@ struct weston_output {
struct weston_mode *original_mode;
struct wl_list mode_list;

- 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);
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:28 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Move the responsibility of ensuring the head will work in the enabled
output to the backends.

A compositor cannot enable an output without heads, and removing the
last head from an output automatically disables the output, so attaching
a new head to an enabled output is only possible for clone mode.

Backends headless, rdp, and x11 forbid clone mode by not having an
attach_head hook implemented; fbdev and wayland explicitly deny clone
mode. Only the DRM backend is affected by this change and even that not
yet because MAX_CLONED_CONNECTORS is 1 in the DRM backend.

Also ensure a global is created for the head when attached to an enabled
output, and log it.

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index b302f8d5..0d2af676 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -76,6 +76,9 @@ weston_output_transform_scale_init(struct weston_output *output,
static void
weston_compositor_build_view_list(struct weston_compositor *compositor);

+static char *
+weston_output_create_heads_string(struct weston_output *output);
+
/** Send wl_output events for mode and scale changes
*
* \param head Send on all resources bound to this head.
@@ -4378,6 +4381,14 @@ bind_output(struct wl_client *client,
wl_output_send_done(resource);
}

+static void
+weston_head_add_global(struct weston_head *head)
+{
+ head->global = wl_global_create(head->compositor->wl_display,
+ &wl_output_interface, 3,
+ head, bind_output);
+}
+
/** Remove the global wl_output protocol object
*
* \param head The head whose global to remove.
@@ -4615,7 +4626,7 @@ weston_output_iterate_heads(struct weston_output *output,
return container_of(node, struct weston_head, output_link);
}

-/** Attach a head to an inactive output
+/** Attach a head to an output
*
* \param output The output to attach to.
* \param head The head that is not yet attached.
@@ -4630,7 +4641,7 @@ weston_output_iterate_heads(struct weston_output *output,
*
* 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().
+ * made on weston_output_enable() unless the output was already enabled.
*
* \memberof weston_output
*/
@@ -4638,8 +4649,7 @@ WL_EXPORT int
weston_output_attach_head(struct weston_output *output,
struct weston_head *head)
{
- if (output->enabled)
- return -1;
+ char *head_names;

if (!wl_list_empty(&head->output_link))
return -1;
@@ -4655,6 +4665,15 @@ weston_output_attach_head(struct weston_output *output,
head->output = output;
wl_list_insert(output->head_list.prev, &head->output_link);

+ if (output->enabled) {
+ weston_head_add_global(head);
+
+ head_names = weston_output_create_heads_string(output);
+ weston_log("Output '%s' updated to have head(s) %s\n",
+ output->name, head_names);
+ free(head_names);
+ }
+
return 0;
}

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

- 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_list_for_each(head, &output->head_list, output_link)
+ weston_head_add_global(head);

wl_signal_emit(&compositor->output_created_signal, output);
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:21 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 8e99a477..cf4a5f3f 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
2018-02-16 14:57:29 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Helps debugging.

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index 0d2af676..362074b6 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4693,6 +4693,7 @@ WL_EXPORT void
weston_head_detach(struct weston_head *head)
{
struct weston_output *output = head->output;
+ char *head_names;

wl_list_remove(&head->output_link);
wl_list_init(&head->output_link);
@@ -4707,8 +4708,16 @@ weston_head_detach(struct weston_head *head)
if (output->enabled) {
weston_head_remove_global(head);

- if (wl_list_empty(&output->head_list))
+ if (wl_list_empty(&output->head_list)) {
+ weston_log("Output '%s' no heads left, disabling.\n",
+ output->name);
weston_output_disable(output);
+ } else {
+ head_names = weston_output_create_heads_string(output);
+ weston_log("Output '%s' updated to have head(s) %s\n",
+ output->name, head_names);
+ free(head_names);
+ }
}
}
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:30 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Switch drm_output_find_by_connector() to search for the output by
iterating the compositor's head_list. drm_head_find_by_connector() will
be useful later on its own.

As of "compositor-drm: start migration to head-based output API" the
head list is guaranteed to contain all created drm_outputs through the
automatically created drm_head.

This simplifies the code a little, introduces
drm_head_find_by_connector(), and works towards the eventual removal of
drm_output_find_by_connector().

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 58aae1ac..78fce5d6 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -804,21 +804,31 @@ drm_output_find_by_crtc(struct drm_backend *b, uint32_t crtc_id)
return NULL;
}

-static struct drm_output *
-drm_output_find_by_connector(struct drm_backend *b, uint32_t connector_id)
+static struct drm_head *
+drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id)
{
- struct drm_output *output;
+ struct weston_head *base;
+ struct drm_head *head;

- wl_list_for_each(output, &b->compositor->output_list, base.link) {
- if (output->connector_id == connector_id)
- return output;
+ wl_list_for_each(base,
+ &backend->compositor->head_list, compositor_link) {
+ head = to_drm_head(base);
+ if (head->output && head->output->connector_id == connector_id)
+ return head;
}

- wl_list_for_each(output, &b->compositor->pending_output_list,
- base.link) {
- if (output->connector_id == connector_id)
- return output;
- }
+ return NULL;
+}
+
+static struct drm_output *
+drm_output_find_by_connector(struct drm_backend *b, uint32_t connector_id)
+{
+ struct drm_head *head;
+
+ /* XXX: like the old version, this counts both enabled and disabled outputs */
+ head = drm_head_find_by_connector(b, connector_id);
+ if (head && head->output)
+ return head->output;

return NULL;
}
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:31 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Making this function not depend on drm_head::output field through
drm_output_find_by_connector() will later allow to remove the
drm_head::output field before removing the unused_connectors array. This
helps keeping the commit more fine-grained.

drm_backend_update_unused_outputs() was only interested in enabled
outputs. The new code is 100% equivalent to the old code. The
difference is that weston_head::output is only set for attached heads. A
connector cannot be in use if it is not attached to an output.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 78fce5d6..67fda913 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4959,11 +4959,11 @@ drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources)
wl_array_init(&b->unused_connectors);

for (i = 0; i < resources->count_connectors; i++) {
- struct drm_output *output;
+ struct drm_head *head;
uint32_t *connector_id;

- output = drm_output_find_by_connector(b, resources->connectors[i]);
- if (output && output->base.enabled)
+ head = drm_head_find_by_connector(b, resources->connectors[i]);
+ if (head && weston_head_is_enabled(&head->base))
continue;

connector_id = wl_array_add(&b->unused_connectors,
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:32 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Instead of iterating output_list and pending_output_list, iterate
head_list to find outputs whose connectors have been disconnected.

This helps a following patch to move connector fields from drm_output to
drm_head.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 67fda913..75820c5b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -5215,7 +5215,8 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
{
drmModeConnector *connector;
drmModeRes *resources;
- struct drm_output *output, *next;
+ struct weston_head *base, *next;
+ struct drm_head *head;
uint32_t *connected;
int i;

@@ -5256,30 +5257,17 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
weston_log("connector %d connected\n", connector_id);
}

- wl_list_for_each_safe(output, next, &b->compositor->output_list,
- base.link) {
+ wl_list_for_each_safe(base, next,
+ &b->compositor->head_list, compositor_link) {
bool disconnected = true;

- for (i = 0; i < resources->count_connectors; i++) {
- if (connected[i] == output->connector_id) {
- disconnected = false;
- break;
- }
- }
+ head = to_drm_head(base);

- if (!disconnected)
+ if (!head->output)
continue;

- weston_log("connector %d disconnected\n", output->connector_id);
- drm_output_destroy(&output->base);
- }
-
- wl_list_for_each_safe(output, next, &b->compositor->pending_output_list,
- base.link) {
- bool disconnected = true;
-
for (i = 0; i < resources->count_connectors; i++) {
- if (connected[i] == output->connector_id) {
+ if (connected[i] == head->output->connector_id) {
disconnected = false;
break;
}
@@ -5288,8 +5276,8 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
if (!disconnected)
continue;

- weston_log("connector %d disconnected\n", output->connector_id);
- drm_output_destroy(&output->base);
+ weston_log("connector %d disconnected\n", head->output->connector_id);
+ drm_output_destroy(&head->output->base);
}

drm_backend_update_unused_outputs(b, resources);
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:34 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Move the connector related fields from drm_output to the drm_head. A
drm_head represents a connector for now.

The code in drm_head_create() to update connector data, monitor
information, etc. is moved into a new function. This will be useful when
DRM-backend starts creating heads for all connectors regardless of their
connection status and will need to update them on hotplug events.

v6:
- adapt to the new places of updating unused_connectors
- free connector in create_output_for_connector() error path

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d386a0d2..63295d2a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -409,20 +409,22 @@ struct drm_head {

struct drm_output *output; /* XXX: temporary */

+ drmModeConnector *connector;
+ uint32_t connector_id;
+ struct drm_edid edid;
+
+ /* Holds the properties for the connector */
+ struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT];
+
struct backlight *backlight;
};

struct drm_output {
struct weston_output base;
- drmModeConnector *connector;

uint32_t crtc_id; /* object ID to pass to DRM functions */
int pipe; /* index of CRTC in resource array / bitmasks */
- uint32_t connector_id;
- struct drm_edid edid;

- /* Holds the properties for the connector */
- struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT];
/* Holds the properties for the CRTC */
struct drm_property_info props_crtc[WDRM_CRTC__COUNT];

@@ -813,7 +815,7 @@ drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id)
wl_list_for_each(base,
&backend->compositor->head_list, compositor_link) {
head = to_drm_head(base);
- if (head->output && head->output->connector_id == connector_id)
+ if (head->connector_id == connector_id)
return head;
}

@@ -1836,10 +1838,11 @@ static int
drm_output_apply_state_legacy(struct drm_output_state *state)
{
struct drm_output *output = state->output;
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(&output->base));
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_property_info *dpms_prop =
- &output->props_conn[WDRM_CONNECTOR_DPMS];
+ &head->props_conn[WDRM_CONNECTOR_DPMS];
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
@@ -1880,7 +1883,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
}

ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
- &output->connector_id, 0, NULL);
+ &head->connector_id, 0, NULL);
if (ret)
weston_log("drmModeSetCrtc failed disabling: %m\n");

@@ -1915,7 +1918,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
0, 0,
- &output->connector_id, 1,
+ &head->connector_id, 1,
&mode->mode_info);
if (ret) {
weston_log("set mode failed: %m\n");
@@ -1988,7 +1991,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)

if (dpms_prop->prop_id && state->dpms != output->state_cur->dpms) {
ret = drmModeConnectorSetProperty(backend->drm.fd,
- output->connector_id,
+ head->connector_id,
dpms_prop->prop_id,
state->dpms);
if (ret) {
@@ -2024,16 +2027,16 @@ crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output,
}

static int
-connector_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+connector_add_prop(drmModeAtomicReq *req, struct drm_head *head,
enum wdrm_connector_property prop, uint64_t val)
{
- struct drm_property_info *info = &output->props_conn[prop];
+ struct drm_property_info *info = &head->props_conn[prop];
int ret;

if (info->prop_id == 0)
return -1;

- ret = drmModeAtomicAddProperty(req, output->connector_id,
+ ret = drmModeAtomicAddProperty(req, head->connector_id,
info->prop_id, val);
return (ret <= 0) ? -1 : 0;
}
@@ -2077,6 +2080,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
uint32_t *flags)
{
struct drm_output *output = state->output;
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(&output->base));
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
@@ -2093,13 +2097,12 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
current_mode->blob_id);
ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
- ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID,
output->crtc_id);
} else {
ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
- ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
- 0);
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
}

if (ret != 0) {
@@ -4314,8 +4317,7 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)

/** Parse monitor make, model and serial from EDID
*
- * \param b The backend instance.
- * \param output The output whose \c drm_edid to fill in.
+ * \param head The head whose \c drm_edid to fill in.
* \param props The DRM connector properties to get the EDID from.
* \param make[out] The monitor make (PNP ID).
* \param model[out] The monitor model (name).
@@ -4324,10 +4326,10 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
* Each of \c *make, \c *model and \c *serial_number are set only if the
* information is found in the EDID. The pointers they are set to must not
* be free()'d explicitly, instead they get implicitly freed when the
- * \c drm_output is destroyed.
+ * \c drm_head is destroyed.
*/
static void
-find_and_parse_output_edid(struct drm_backend *b, struct drm_output *output,
+find_and_parse_output_edid(struct drm_head *head,
drmModeObjectPropertiesPtr props,
const char **make,
const char **model,
@@ -4338,29 +4340,29 @@ find_and_parse_output_edid(struct drm_backend *b, struct drm_output *output,
int rc;

blob_id =
- drm_property_get_value(&output->props_conn[WDRM_CONNECTOR_EDID],
+ drm_property_get_value(&head->props_conn[WDRM_CONNECTOR_EDID],
props, 0);
if (!blob_id)
return;

- edid_blob = drmModeGetPropertyBlob(b->drm.fd, blob_id);
+ edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id);
if (!edid_blob)
return;

- rc = edid_parse(&output->edid,
+ rc = edid_parse(&head->edid,
edid_blob->data,
edid_blob->length);
if (!rc) {
weston_log("EDID data '%s', '%s', '%s'\n",
- output->edid.pnp_id,
- output->edid.monitor_name,
- output->edid.serial_number);
- if (output->edid.pnp_id[0] != '\0')
- *make = output->edid.pnp_id;
- if (output->edid.monitor_name[0] != '\0')
- *model = output->edid.monitor_name;
- if (output->edid.serial_number[0] != '\0')
- *serial_number = output->edid.serial_number;
+ head->edid.pnp_id,
+ head->edid.monitor_name,
+ head->edid.serial_number);
+ if (head->edid.pnp_id[0] != '\0')
+ *make = head->edid.pnp_id;
+ if (head->edid.monitor_name[0] != '\0')
+ *model = head->edid.monitor_name;
+ if (head->edid.serial_number[0] != '\0')
+ *serial_number = head->edid.serial_number;
}
drmModeFreePropertyBlob(edid_blob);
}
@@ -4587,11 +4589,12 @@ drm_output_set_mode(struct weston_output *base,
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(base));

struct drm_mode *current;
drmModeModeInfo crtc_mode;

- if (connector_get_current_mode(output->connector, b->drm.fd, &crtc_mode) < 0)
+ if (connector_get_current_mode(head->connector, b->drm.fd, &crtc_mode) < 0)
return -1;

current = drm_output_choose_initial_mode(b, output, mode, modeline, &crtc_mode);
@@ -4806,11 +4809,11 @@ drm_output_enable(struct weston_output *base)
&output->scanout_plane->base,
&b->compositor->primary_plane);

- wl_array_remove_uint32(&b->unused_connectors, output->connector_id);
+ wl_array_remove_uint32(&b->unused_connectors, head->connector_id);
wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id);

weston_log("Output %s, (connector %d, crtc %d)\n",
- output->base.name, output->connector_id, output->crtc_id);
+ output->base.name, head->connector_id, output->crtc_id);
wl_list_for_each(m, &output->base.mode_list, link)
weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f%s%s%s\n",
m->width, m->height, m->refresh / 1000.0,
@@ -4818,7 +4821,7 @@ drm_output_enable(struct weston_output *base)
", preferred" : "",
m->flags & WL_OUTPUT_MODE_CURRENT ?
", current" : "",
- output->connector->count_modes == 0 ?
+ head->connector->count_modes == 0 ?
", built-in" : "");

return 0;
@@ -4831,6 +4834,7 @@ static void
drm_output_deinit(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
struct drm_backend *b = to_drm_backend(base->compositor);
uint32_t *unused;

@@ -4856,7 +4860,7 @@ drm_output_deinit(struct weston_output *base)
}

unused = wl_array_add(&b->unused_connectors, sizeof(*unused));
- *unused = output->connector_id;
+ *unused = head->connector_id;
unused = wl_array_add(&b->unused_crtcs, sizeof(*unused));
*unused = output->crtc_id;

@@ -4893,9 +4897,6 @@ drm_output_destroy(struct weston_output *base)

drm_output_fini_crtc(output);

- drm_property_info_free(output->props_conn, WDRM_CONNECTOR__COUNT);
- drmModeFreeConnector(output->connector);
-
assert(!output->state_last);
drm_output_state_free(output->state_cur);

@@ -4985,6 +4986,62 @@ drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources)
}
}

+/** Replace connector data and monitor information
+ *
+ * @param head The head to update.
+ * @param connector The connector data to be owned by the head, must match
+ * the head's connector ID.
+ * @return 0 on success, -1 on failure.
+ *
+ * Takes ownership of @c connector on success, not on failure.
+ *
+ * May schedule a heads changed call.
+ */
+static int
+drm_head_assign_connector_info(struct drm_head *head,
+ drmModeConnector *connector)
+{
+ drmModeObjectProperties *props;
+ const char *make = "unknown";
+ const char *model = "unknown";
+ const char *serial_number = "unknown";
+
+ assert(connector);
+ assert(head->connector_id == connector->connector_id);
+
+ props = drmModeObjectGetProperties(head->backend->drm.fd,
+ head->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ weston_log("Error: failed to get connector '%s' properties\n",
+ head->base.name);
+ return -1;
+ }
+
+ if (head->connector)
+ drmModeFreeConnector(head->connector);
+ head->connector = connector;
+
+ drm_property_info_populate(head->backend, connector_props,
+ head->props_conn,
+ WDRM_CONNECTOR__COUNT, props);
+ find_and_parse_output_edid(head, props, &make, &model, &serial_number);
+ weston_head_set_monitor_strings(&head->base, make, model, serial_number);
+ weston_head_set_subpixel(&head->base,
+ drm_subpixel_to_wayland(head->connector->subpixel));
+
+ weston_head_set_physical_size(&head->base, head->connector->mmWidth,
+ head->connector->mmHeight);
+
+ drmModeFreeObjectProperties(props);
+
+ /* Unknown connection status is assumed disconnected. */
+ weston_head_set_connection_status(&head->base,
+ head->connector->connection == DRM_MODE_CONNECTED);
+
+ return 0;
+}
+
/**
* Create a Weston head for a connector
*
@@ -5019,23 +5076,29 @@ drm_head_create(struct drm_backend *backend, uint32_t connector_id,
weston_head_init(&head->base, name);
free(name);

+ head->connector_id = connector_id;
head->backend = backend;

head->backlight = backlight_init(drm_device, connector->connector_type);

- /* Unknown connection status is assumed disconnected. */
- weston_head_set_connection_status(&head->base,
- connector->connection == DRM_MODE_CONNECTED);
+ if (drm_head_assign_connector_info(head, connector) < 0)
+ goto err_init;
+
+ if (head->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
+ head->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ weston_head_set_internal(&head->base);

weston_compositor_add_head(backend->compositor, &head->base);
- drmModeFreeConnector(connector);

weston_log("DRM: found head '%s', connector %d %s.\n",
- head->base.name, connector_id,
+ head->base.name, head->connector_id,
head->base.connected ? "connected" : "disconnected");

return head;

+err_init:
+ weston_head_release(&head->base);
+
err_alloc:
if (connector)
drmModeFreeConnector(connector);
@@ -5050,6 +5113,9 @@ drm_head_destroy(struct drm_head *head)
{
weston_head_release(&head->base);

+ drm_property_info_free(head->props_conn, WDRM_CONNECTOR__COUNT);
+ drmModeFreeConnector(head->connector);
+
if (head->backlight)
backlight_destroy(head->backlight);

@@ -5077,31 +5143,21 @@ create_output_for_connector(struct drm_backend *b,
{
struct drm_output *output;
struct drm_head *head;
- drmModeObjectPropertiesPtr props;
struct drm_mode *drm_mode;
- char *name;
- const char *make = "unknown";
- const char *model = "unknown";
- const char *serial_number = "unknown";
int i;

output = zalloc(sizeof *output);
if (output == NULL)
goto err_init;

- output->connector = connector;
- output->connector_id = connector->connector_id;
-
- name = make_connector_name(connector);
- weston_output_init(&output->base, b->compositor, name);
- free(name);
-
/* XXX: temporary */
head = drm_head_create(b, connector->connector_id, drm_device);
if (!head)
abort();
head->output = output;

+ weston_output_init(&output->base, b->compositor, head->base.name);
+
output->base.enable = drm_output_enable;
output->base.destroy = drm_output_destroy;
output->base.disable = drm_output_disable;
@@ -5113,36 +5169,13 @@ create_output_for_connector(struct drm_backend *b,
if (drm_output_init_crtc(output, resources, connector) < 0)
goto err_output;

- props = drmModeObjectGetProperties(b->drm.fd, connector->connector_id,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!props) {
- weston_log("failed to get connector properties\n");
- goto err_output;
- }
- drm_property_info_populate(b, connector_props, output->props_conn,
- WDRM_CONNECTOR__COUNT, props);
- find_and_parse_output_edid(b, output, props,
- &make, &model, &serial_number);
- weston_head_set_monitor_strings(&head->base, make, model, serial_number);
- weston_head_set_subpixel(&head->base,
- drm_subpixel_to_wayland(output->connector->subpixel));
-
- drmModeFreeObjectProperties(props);
-
- if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
- output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- weston_head_set_internal(&head->base);
-
if (drm_output_init_gamma_size(output) < 0)
goto err_output;

- weston_head_set_physical_size(&head->base, output->connector->mmWidth,
- output->connector->mmHeight);
-
output->state_cur = drm_output_state_alloc(output, NULL);

- for (i = 0; i < output->connector->count_modes; i++) {
- drm_mode = drm_output_add_mode(output, &output->connector->modes[i]);
+ for (i = 0; i < head->connector->count_modes; i++) {
+ drm_mode = drm_output_add_mode(output, &head->connector->modes[i]);
if (!drm_mode) {
weston_log("failed to add mode\n");
goto err_output;
@@ -5151,13 +5184,14 @@ create_output_for_connector(struct drm_backend *b,

weston_compositor_add_pending_output(&output->base, b->compositor);

+ /* drm_head_create() made its own connector */
+ drmModeFreeConnector(connector);
+
return 0;

err_output:
drm_head_destroy(head);
drm_output_destroy(&output->base);
- return -1;
- /* no fallthrough! */

err_init:
drmModeFreeConnector(connector);
@@ -5268,7 +5302,7 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
continue;

for (i = 0; i < resources->count_connectors; i++) {
- if (connected[i] == head->output->connector_id) {
+ if (connected[i] == head->connector_id) {
disconnected = false;
break;
}
@@ -5277,7 +5311,7 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
if (!disconnected)
continue;

- weston_log("connector %d disconnected\n", head->output->connector_id);
+ weston_log("connector %d disconnected\n", head->connector_id);
drm_output_destroy(&head->output->base);
}
--
2.13.6
Daniel Stone
2018-04-12 12:03:32 UTC
Permalink
Hi Pekka,
I've reviewed up to this point (the first half as discussed before,
second half from this submission), and it all looks good so far. One
Post by Pekka Paalanen
+/** Replace connector data and monitor information
+ *
+ * the head's connector ID.
+ *
+ *
+ * May schedule a heads changed call.
+ */
+static int
+drm_head_assign_connector_info(struct drm_head *head,
+ drmModeConnector *connector)
+{
+ drmModeObjectProperties *props;
+ const char *make = "unknown";
+ const char *model = "unknown";
+ const char *serial_number = "unknown";
+
+ assert(connector);
+ assert(head->connector_id == connector->connector_id);
+
+ props = drmModeObjectGetProperties(head->backend->drm.fd,
+ head->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ weston_log("Error: failed to get connector '%s' properties\n",
+ head->base.name);
+ return -1;
+ }
+
+ if (head->connector)
+ drmModeFreeConnector(head->connector);
+ head->connector = connector;
One thing I'm missing (possibly post-lunch tiredness, but): when
exactly would we replace a connector? Is it only if we have a
hot-unplugged connector which later appears as a connector of the same
name? e.g. DP-4 as MST disappears, and then reappears with the same
name but a different connector ID.

This bit is slightly confusing to me, but I'm pretty sure I've
followed the rest and it's looking good.

Cheers,
Daniel
Pekka Paalanen
2018-04-12 12:37:43 UTC
Permalink
On Thu, 12 Apr 2018 14:03:32 +0200
Post by Daniel Stone
Hi Pekka,
I've reviewed up to this point (the first half as discussed before,
second half from this submission), and it all looks good so far. One
Post by Pekka Paalanen
+/** Replace connector data and monitor information
+ *
+ * the head's connector ID.
+ *
+ *
+ * May schedule a heads changed call.
+ */
+static int
+drm_head_assign_connector_info(struct drm_head *head,
+ drmModeConnector *connector)
+{
+ drmModeObjectProperties *props;
+ const char *make = "unknown";
+ const char *model = "unknown";
+ const char *serial_number = "unknown";
+
+ assert(connector);
+ assert(head->connector_id == connector->connector_id);
+
+ props = drmModeObjectGetProperties(head->backend->drm.fd,
+ head->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ weston_log("Error: failed to get connector '%s' properties\n",
+ head->base.name);
+ return -1;
+ }
+
+ if (head->connector)
+ drmModeFreeConnector(head->connector);
+ head->connector = connector;
One thing I'm missing (possibly post-lunch tiredness, but): when
exactly would we replace a connector? Is it only if we have a
hot-unplugged connector which later appears as a connector of the same
name? e.g. DP-4 as MST disappears, and then reappears with the same
name but a different connector ID.
Every time there is any hotplug event, all connectors' information is
reloaded. The information could have changed without us seeing the
connector disconnected in between. This runs by connector IDs, not
names.

drm_head is always created for all connectors we find, regardless of
connected or not.

drm_head_assign_connector_info() deals with disconnected connectors as
well, so it handles also the connected/disconnected changes. After all,
drmModeGetConnector() must be called anyway to see if the connector got
dis/connected.

To avoid triggering a heads changed when nothing actually changed, the
weston_head_set_*() functions check if the data really changed.
Post by Daniel Stone
This bit is slightly confusing to me, but I'm pretty sure I've
followed the rest and it's looking good.
Cool, thanks,
pq
Pekka Paalanen
2018-04-12 12:53:30 UTC
Permalink
On Thu, 12 Apr 2018 15:37:43 +0300
Post by Pekka Paalanen
On Thu, 12 Apr 2018 14:03:32 +0200
Post by Daniel Stone
Hi Pekka,
I've reviewed up to this point (the first half as discussed before,
second half from this submission), and it all looks good so far. One
Post by Pekka Paalanen
+/** Replace connector data and monitor information
+ *
+ * the head's connector ID.
+ *
+ *
+ * May schedule a heads changed call.
+ */
+static int
+drm_head_assign_connector_info(struct drm_head *head,
+ drmModeConnector *connector)
+{
+ drmModeObjectProperties *props;
+ const char *make = "unknown";
+ const char *model = "unknown";
+ const char *serial_number = "unknown";
+
+ assert(connector);
+ assert(head->connector_id == connector->connector_id);
+
+ props = drmModeObjectGetProperties(head->backend->drm.fd,
+ head->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ weston_log("Error: failed to get connector '%s' properties\n",
+ head->base.name);
+ return -1;
+ }
+
+ if (head->connector)
+ drmModeFreeConnector(head->connector);
+ head->connector = connector;
One thing I'm missing (possibly post-lunch tiredness, but): when
exactly would we replace a connector? Is it only if we have a
hot-unplugged connector which later appears as a connector of the same
name? e.g. DP-4 as MST disappears, and then reappears with the same
name but a different connector ID.
Every time there is any hotplug event, all connectors' information is
reloaded. The information could have changed without us seeing the
connector disconnected in between. This runs by connector IDs, not
names.
drm_head is always created for all connectors we find, regardless of
connected or not.
Oops, I suppose this happens at a later step in the series. I think my
explanation should still apply even at this step though, for the missed
unplug event part.


Thanks,
pq
Post by Pekka Paalanen
drm_head_assign_connector_info() deals with disconnected connectors as
well, so it handles also the connected/disconnected changes. After all,
drmModeGetConnector() must be called anyway to see if the connector got
dis/connected.
To avoid triggering a heads changed when nothing actually changed, the
weston_head_set_*() functions check if the data really changed.
Post by Daniel Stone
This bit is slightly confusing to me, but I'm pretty sure I've
followed the rest and it's looking good.
Cool, thanks,
pq
Daniel Stone
2018-04-12 12:58:13 UTC
Permalink
Hi Pekka,
Post by Pekka Paalanen
Post by Pekka Paalanen
Post by Daniel Stone
One thing I'm missing (possibly post-lunch tiredness, but): when
exactly would we replace a connector? Is it only if we have a
hot-unplugged connector which later appears as a connector of the same
name? e.g. DP-4 as MST disappears, and then reappears with the same
name but a different connector ID.
Every time there is any hotplug event, all connectors' information is
reloaded. The information could have changed without us seeing the
connector disconnected in between. This runs by connector IDs, not
names.
drm_head is always created for all connectors we find, regardless of
connected or not.
Oops, I suppose this happens at a later step in the series. I think my
explanation should still apply even at this step though, for the missed
unplug event part.
Cool, that's what I was hoping the answer was. It does make sense
later on in the series; it was just at this point that I was worried I
was missing something. I don't think there's much point in reshuffling
it, but maybe a very small comment in the commit message would be
useful.

Cheers,
Daniel
Pekka Paalanen
2018-02-16 14:57:33 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Backlight is driven per connector, hence it belongs in struct drm_head.

weston_output::set_backlight() API is remains per output so far. There
is no UI to control backlights per head and adding one would be
difficult for an output that has multiple cloned heads.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 75820c5b..d386a0d2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -408,6 +408,8 @@ struct drm_head {
struct drm_backend *backend;

struct drm_output *output; /* XXX: temporary */
+
+ struct backlight *backlight;
};

struct drm_output {
@@ -424,8 +426,6 @@ struct drm_output {
/* Holds the properties for the CRTC */
struct drm_property_info props_crtc[WDRM_CRTC__COUNT];

- struct backlight *backlight;
-
int vblank_pending;
int page_flip_pending;
int atomic_complete_pending;
@@ -3826,12 +3826,12 @@ drm_subpixel_to_wayland(int drm_value)

/* returns a value between 0-255 range, where higher is brighter */
static uint32_t
-drm_get_backlight(struct drm_output *output)
+drm_get_backlight(struct drm_head *head)
{
long brightness, max_brightness, norm;

- brightness = backlight_get_brightness(output->backlight);
- max_brightness = backlight_get_max_brightness(output->backlight);
+ brightness = backlight_get_brightness(head->backlight);
+ max_brightness = backlight_get_max_brightness(head->backlight);

/* convert it on a scale of 0 to 255 */
norm = (brightness * 255)/(max_brightness);
@@ -3843,21 +3843,21 @@ drm_get_backlight(struct drm_output *output)
static void
drm_set_backlight(struct weston_output *output_base, uint32_t value)
{
- struct drm_output *output = to_drm_output(output_base);
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(output_base));
long max_brightness, new_brightness;

- if (!output->backlight)
+ if (!head->backlight)
return;

if (value > 255)
return;

- max_brightness = backlight_get_max_brightness(output->backlight);
+ max_brightness = backlight_get_max_brightness(head->backlight);

/* get denormalized value */
new_brightness = (value * max_brightness) / 255;

- backlight_set_brightness(output->backlight, new_brightness);
+ backlight_set_brightness(head->backlight, new_brightness);
}

/**
@@ -4763,6 +4763,7 @@ drm_output_enable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
struct weston_mode *m;

if (b->pageflip_timeout)
@@ -4778,11 +4779,11 @@ drm_output_enable(struct weston_output *base)
goto err;
}

- if (output->backlight) {
+ if (head->backlight) {
weston_log("Initialized backlight, device %s\n",
- output->backlight->path);
+ head->backlight->path);
output->base.set_backlight = drm_set_backlight;
- output->base.backlight_current = drm_get_backlight(output);
+ output->base.backlight_current = drm_get_backlight(head);
} else {
weston_log("Failed to initialize backlight\n");
}
@@ -4895,9 +4896,6 @@ drm_output_destroy(struct weston_output *base)
drm_property_info_free(output->props_conn, WDRM_CONNECTOR__COUNT);
drmModeFreeConnector(output->connector);

- if (output->backlight)
- backlight_destroy(output->backlight);
-
assert(!output->state_last);
drm_output_state_free(output->state_cur);

@@ -5023,6 +5021,8 @@ drm_head_create(struct drm_backend *backend, uint32_t connector_id,

head->backend = backend;

+ head->backlight = backlight_init(drm_device, connector->connector_type);
+
/* Unknown connection status is assumed disconnected. */
weston_head_set_connection_status(&head->base,
connector->connection == DRM_MODE_CONNECTED);
@@ -5049,6 +5049,10 @@ static void
drm_head_destroy(struct drm_head *head)
{
weston_head_release(&head->base);
+
+ if (head->backlight)
+ backlight_destroy(head->backlight);
+
free(head);
}

@@ -5088,9 +5092,6 @@ create_output_for_connector(struct drm_backend *b,
output->connector = connector;
output->connector_id = connector->connector_id;

- output->backlight = backlight_init(drm_device,
- connector->connector_type);
-
name = make_connector_name(connector);
weston_output_init(&output->base, b->compositor, name);
free(name);
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:36 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

As CRTC is allocated on output enable and deallocated on output disable,
there cannot be any matches in find-by-crtc from the
pending_output_list.

Remove the loop over pending_output_list as never finding anything by
definition.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index dc4f5db5..f38766c5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -797,12 +797,6 @@ drm_output_find_by_crtc(struct drm_backend *b, uint32_t crtc_id)
return output;
}

- wl_list_for_each(output, &b->compositor->pending_output_list,
- base.link) {
- if (output->crtc_id == crtc_id)
- return output;
- }
-
return NULL;
}
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:35 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

A drm_output needs a CRTC only when it is in use. Allocating a CRTC on
creation of drm_output will reserve the CRTC regardless of whether the
output is actually used or not. This may cause creating other
drm_outputs to fail if there are not enough CRTCs.

Instead, allocate the CRTC on drm_output enable() time. A drm_output
will have a valid CRTC only while it is enabled.

This allows us to create drm_output objects arbitrarily and without a
head assignment, which is required by the head-based output API for the
backends. The assigned heads will be known only at enable() time.

Now drm_output_enable() has to call drmModeGetResources() to be able to
find a suitable CRTC. We might want to cache the resources somewhere,
but that is it topic for another patch.

v4: Force resetting unused CRTCs on fini.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 63295d2a..dc4f5db5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4716,6 +4716,8 @@ drm_output_init_crtc(struct drm_output *output,
drm_output_find_special_plane(b, output,
WDRM_PLANE_TYPE_CURSOR);

+ wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id);
+
return 0;

err_crtc:
@@ -4735,6 +4737,7 @@ static void
drm_output_fini_crtc(struct drm_output *output)
{
struct drm_backend *b = to_drm_backend(output->base.compositor);
+ uint32_t *unused;

if (!b->universal_planes && !b->shutting_down) {
/* With universal planes, the 'special' planes are allocated at
@@ -4756,6 +4759,15 @@ drm_output_fini_crtc(struct drm_output *output)
}

drm_property_info_free(output->props_crtc, WDRM_CRTC__COUNT);
+
+ assert(output->crtc_id != 0);
+
+ unused = wl_array_add(&b->unused_crtcs, sizeof(*unused));
+ *unused = output->crtc_id;
+
+ /* Force resetting unused CRTCs */
+ b->state_invalid = true;
+
output->crtc_id = 0;
output->cursor_plane = NULL;
output->scanout_plane = NULL;
@@ -4768,6 +4780,21 @@ drm_output_enable(struct weston_output *base)
struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
struct weston_mode *m;
+ drmModeRes *resources;
+ int ret;
+
+ resources = drmModeGetResources(b->drm.fd);
+ if (!resources) {
+ weston_log("drmModeGetResources failed\n");
+ return -1;
+ }
+ ret = drm_output_init_crtc(output, resources, head->connector);
+ drmModeFreeResources(resources);
+ if (ret < 0)
+ return -1;
+
+ if (drm_output_init_gamma_size(output) < 0)
+ goto err;

if (b->pageflip_timeout)
drm_output_pageflip_timer_create(output);
@@ -4810,7 +4837,6 @@ drm_output_enable(struct weston_output *base)
&b->compositor->primary_plane);

wl_array_remove_uint32(&b->unused_connectors, head->connector_id);
- wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id);

weston_log("Output %s, (connector %d, crtc %d)\n",
output->base.name, head->connector_id, output->crtc_id);
@@ -4827,6 +4853,8 @@ drm_output_enable(struct weston_output *base)
return 0;

err:
+ drm_output_fini_crtc(output);
+
return -1;
}

@@ -4861,11 +4889,8 @@ drm_output_deinit(struct weston_output *base)

unused = wl_array_add(&b->unused_connectors, sizeof(*unused));
*unused = head->connector_id;
- unused = wl_array_add(&b->unused_crtcs, sizeof(*unused));
- *unused = output->crtc_id;

- /* Force programming unused connectors and crtcs. */
- b->state_invalid = true;
+ drm_output_fini_crtc(output);
}

static void
@@ -4895,8 +4920,6 @@ drm_output_destroy(struct weston_output *base)

weston_output_release(&output->base);

- drm_output_fini_crtc(output);
-
assert(!output->state_last);
drm_output_state_free(output->state_cur);

@@ -5166,12 +5189,6 @@ create_output_for_connector(struct drm_backend *b,
output->destroy_pending = 0;
output->disable_pending = 0;

- if (drm_output_init_crtc(output, resources, connector) < 0)
- goto err_output;
-
- if (drm_output_init_gamma_size(output) < 0)
- goto err_output;
-
output->state_cur = drm_output_state_alloc(output, NULL);

for (i = 0; i < head->connector->count_modes; i++) {
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:37 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

As these planes are allocated on output enable and freed on output
disable, there cannot be a match in the pending_output_list.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index f38766c5..c816d008 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3614,14 +3614,6 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
/* On some platforms, primary/cursor planes can roam
* between different CRTCs, so make sure we don't claim the
* same plane for two outputs. */
- wl_list_for_each(tmp, &b->compositor->pending_output_list,
- base.link) {
- if (tmp->cursor_plane == plane ||
- tmp->scanout_plane == plane) {
- found_elsewhere = true;
- break;
- }
- }
wl_list_for_each(tmp, &b->compositor->output_list,
base.link) {
if (tmp->cursor_plane == plane ||
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:38 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The inherited mode is the video mode on the connector when we have not
yet reconfigured the connector, if set.

Get the inherited mode the moment we create a drm_head, not when we
determine the mode for a drm_output. This way we are sure to read all
inherited modes before we reconfigure a single CRTC. Enabling one output
may grab the CRTC from another connector, overwriting whatever mode that
connector might have had.

The inherited mode is stored in drm_head, where we can keep it for the
lifetime of the head, rather than relying on re-loading it from the
kernel at set_mode() time.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index c816d008..5e459380 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -417,6 +417,8 @@ struct drm_head {
struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT];

struct backlight *backlight;
+
+ drmModeModeInfo inherited_mode; /**< Original mode on the connector */
};

struct drm_output {
@@ -4578,12 +4580,9 @@ drm_output_set_mode(struct weston_output *base,
struct drm_head *head = to_drm_head(weston_output_get_first_head(base));

struct drm_mode *current;
- drmModeModeInfo crtc_mode;
-
- if (connector_get_current_mode(head->connector, b->drm.fd, &crtc_mode) < 0)
- return -1;

- current = drm_output_choose_initial_mode(b, output, mode, modeline, &crtc_mode);
+ current = drm_output_choose_initial_mode(b, output, mode, modeline,
+ &head->inherited_mode);
if (!current)
return -1;

@@ -5097,6 +5096,13 @@ drm_head_create(struct drm_backend *backend, uint32_t connector_id,
head->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
weston_head_set_internal(&head->base);

+ if (connector_get_current_mode(head->connector, backend->drm.fd,
+ &head->inherited_mode) < 0) {
+ weston_log("Failed to retrieve current mode from connector %d.\n",
+ head->connector_id);
+ /* Continue, inherited_mode was memset to zero. */
+ }
+
weston_compositor_add_head(backend->compositor, &head->base);

weston_log("DRM: found head '%s', connector %d %s.\n",
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:39 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Move the initialization of the drm_output mode list to
drm_output_set_mode() time.

Once we stop creating the drm_head with the drm_output, there will not
be a head to get the mode list from at drm_output creation time.

Furthermore, once DRM-backend starts supporting more than one head per
output, the combined mode list to be exposed to clients (and the
compositor?) must be constructed with all heads attached.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5e459380..c0687932 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4457,6 +4457,43 @@ parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
return ret;
}

+/** Rewrite the output's mode list
+ *
+ * @param output The output.
+ * @return 0 on success, -1 on failure.
+ *
+ * Destroy all existing modes in the list, and reconstruct a new list from
+ * scratch, based on the currently attached heads.
+ *
+ * On failure the output's mode list may contain some modes.
+ */
+static int
+drm_output_update_modelist_from_heads(struct drm_output *output)
+{
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ struct weston_head *head_base;
+ struct drm_head *head;
+ struct drm_mode *mode;
+ int i;
+
+ assert(!output->base.enabled);
+
+ drm_mode_list_destroy(backend, &output->base.mode_list);
+
+ /* XXX: needs a strategy for combining mode lists from multiple heads */
+ head_base = weston_output_get_first_head(&output->base);
+ assert(head_base);
+ head = to_drm_head(head_base);
+
+ for (i = 0; i < head->connector->count_modes; i++) {
+ mode = drm_output_add_mode(output, &head->connector->modes[i]);
+ if (!mode)
+ return -1;
+ }
+
+ return 0;
+}
+
/**
* Choose suitable mode for an output
*
@@ -4581,6 +4618,9 @@ drm_output_set_mode(struct weston_output *base,

struct drm_mode *current;

+ if (drm_output_update_modelist_from_heads(output) < 0)
+ return -1;
+
current = drm_output_choose_initial_mode(b, output, mode, modeline,
&head->inherited_mode);
if (!current)
@@ -5158,8 +5198,6 @@ create_output_for_connector(struct drm_backend *b,
{
struct drm_output *output;
struct drm_head *head;
- struct drm_mode *drm_mode;
- int i;

output = zalloc(sizeof *output);
if (output == NULL)
@@ -5183,14 +5221,6 @@ create_output_for_connector(struct drm_backend *b,

output->state_cur = drm_output_state_alloc(output, NULL);

- for (i = 0; i < head->connector->count_modes; i++) {
- drm_mode = drm_output_add_mode(output, &head->connector->modes[i]);
- if (!drm_mode) {
- weston_log("failed to add mode\n");
- goto err_output;
- }
- }
-
weston_compositor_add_pending_output(&output->base, b->compositor);

/* drm_head_create() made its own connector */
@@ -5198,10 +5228,6 @@ create_output_for_connector(struct drm_backend *b,

return 0;

-err_output:
- drm_head_destroy(head);
- drm_output_destroy(&output->base);
-
err_init:
drmModeFreeConnector(connector);
return -1;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:40 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

In previous patches, all the appropriate fields from drm_output have
been moved into drm_head, and resource allocation has been moved away
from drm_output creation. It is time to throw the switch: this patch
disconnects the drm_output and drm_head lifetimes.

Previously a drm_output was created for a connected connector and
destroyed on disconnection. A drm_head was tied to the drm_output
lifetime just to accommodate the head-based output configuration API
temporarily.

Now all connectors will get a head created regardless of their
connection status. Heads are created and destroyed as connectors appear
and disappear (MST), not when they get connected or disconnected. This
should allow the compositor to force-enable a disconnected connector.

An "empty" drm_output is created with weston_backend::create_output()
hook. This now follows the intent of the head-based output configuration
API.

On hotplug events, all connectors' information is updated regardless of
their connection status changes. It is theoretically possible for a
monitor to change without going through a disconnected state in between.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index c0687932..679f78be 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -407,8 +407,6 @@ struct drm_head {
struct weston_head base;
struct drm_backend *backend;

- struct drm_output *output; /* XXX: temporary */
-
drmModeConnector *connector;
uint32_t connector_id;
struct drm_edid edid;
@@ -818,19 +816,6 @@ drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id)
return NULL;
}

-static struct drm_output *
-drm_output_find_by_connector(struct drm_backend *b, uint32_t connector_id)
-{
- struct drm_head *head;
-
- /* XXX: like the old version, this counts both enabled and disabled outputs */
- head = drm_head_find_by_connector(b, connector_id);
- if (head && head->output)
- return head->output;
-
- return NULL;
-}
-
static void
drm_fb_destroy(struct drm_fb *fb)
{
@@ -4926,7 +4911,6 @@ drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- struct weston_head *head = weston_output_get_first_head(base);

if (output->page_flip_pending || output->vblank_pending ||
output->atomic_complete_pending) {
@@ -4949,10 +4933,6 @@ drm_output_destroy(struct weston_output *base)
drm_output_state_free(output->state_cur);

free(output);
-
- /* XXX: temporary */
- if (head)
- drm_head_destroy(to_drm_head(head));
}

static int
@@ -4976,19 +4956,6 @@ drm_output_disable(struct weston_output *base)
return 0;
}

-static struct weston_output *
-drm_output_create(struct weston_compositor *compositor, const char *name)
-{
- struct drm_head *head;
-
- /* XXX: Temporary until we can have heads without an output */
- wl_list_for_each(head, &compositor->head_list, base.compositor_link)
- if (strcmp(name, head->base.name) == 0)
- return &head->output->base;
-
- return NULL;
-}
-
/**
* Update the list of unused connectors and CRTCs
*
@@ -5090,6 +5057,31 @@ drm_head_assign_connector_info(struct drm_head *head,
return 0;
}

+/** Update connector and monitor information
+ *
+ * @param head The head to update.
+ *
+ * Re-reads the DRM property lists for the connector and updates monitor
+ * information and connection status. This may schedule a heads changed call
+ * to the user.
+ */
+static void
+drm_head_update_info(struct drm_head *head)
+{
+ drmModeConnector *connector;
+
+ connector = drmModeGetConnector(head->backend->drm.fd,
+ head->connector_id);
+ if (!connector) {
+ weston_log("DRM: getting connector info for '%s' failed.\n",
+ head->base.name);
+ return;
+ }
+
+ if (drm_head_assign_connector_info(head, connector) < 0)
+ drmModeFreeConnector(connector);
+}
+
/**
* Create a Weston head for a connector
*
@@ -5180,36 +5172,27 @@ drm_head_destroy(struct drm_head *head)
/**
* Create a Weston output structure
*
- * Given a DRM connector, create a matching drm_output structure and add it
- * to Weston's output list. It also takes ownership of the connector, which
- * is released when output is destroyed.
+ * Create an "empty" drm_output. This is the implementation of
+ * weston_backend::create_output.
*
- * @param b Weston backend structure
- * @param resources DRM resources for this device
- * @param connector DRM connector to use for this new output
- * @param drm_device udev device pointer
- * @returns 0 on success, or -1 on failure
+ * Creating an output is usually followed by drm_output_attach_head()
+ * and drm_output_enable() to make use of it.
+ *
+ * @param compositor The compositor instance.
+ * @param name Name for the new output.
+ * @returns The output, or NULL on failure.
*/
-static int
-create_output_for_connector(struct drm_backend *b,
- drmModeRes *resources,
- drmModeConnector *connector,
- struct udev_device *drm_device)
+static struct weston_output *
+drm_output_create(struct weston_compositor *compositor, const char *name)
{
+ struct drm_backend *b = to_drm_backend(compositor);
struct drm_output *output;
- struct drm_head *head;

output = zalloc(sizeof *output);
if (output == NULL)
- goto err_init;
-
- /* XXX: temporary */
- head = drm_head_create(b, connector->connector_id, drm_device);
- if (!head)
- abort();
- head->output = output;
+ return NULL;

- weston_output_init(&output->base, b->compositor, head->base.name);
+ weston_output_init(&output->base, compositor, name);

output->base.enable = drm_output_enable;
output->base.destroy = drm_output_destroy;
@@ -5223,20 +5206,13 @@ create_output_for_connector(struct drm_backend *b,

weston_compositor_add_pending_output(&output->base, b->compositor);

- /* drm_head_create() made its own connector */
- drmModeFreeConnector(connector);
-
- return 0;
-
-err_init:
- drmModeFreeConnector(connector);
- return -1;
+ return &output->base;
}

static int
-create_outputs(struct drm_backend *b, struct udev_device *drm_device)
+drm_backend_create_heads(struct drm_backend *b, struct udev_device *drm_device)
{
- drmModeConnector *connector;
+ struct drm_head *head;
drmModeRes *resources;
int i;

@@ -5252,42 +5228,28 @@ create_outputs(struct drm_backend *b, struct udev_device *drm_device)
b->max_height = resources->max_height;

for (i = 0; i < resources->count_connectors; i++) {
- int ret;
-
- connector = drmModeGetConnector(b->drm.fd,
- resources->connectors[i]);
- if (connector == NULL)
- continue;
+ uint32_t connector_id = resources->connectors[i];

- if (connector->connection == DRM_MODE_CONNECTED) {
- ret = create_output_for_connector(b, resources,
- connector, drm_device);
- if (ret < 0)
- weston_log("failed to create new connector\n");
- } else {
- drmModeFreeConnector(connector);
+ head = drm_head_create(b, connector_id, drm_device);
+ if (!head) {
+ weston_log("DRM: failed to create head for connector %d.\n",
+ connector_id);
}
}

drm_backend_update_unused_outputs(b, resources);

- if (wl_list_empty(&b->compositor->output_list) &&
- wl_list_empty(&b->compositor->pending_output_list))
- weston_log("No currently active connector found.\n");
-
drmModeFreeResources(resources);

return 0;
}

static void
-update_outputs(struct drm_backend *b, struct udev_device *drm_device)
+drm_backend_update_heads(struct drm_backend *b, struct udev_device *drm_device)
{
- drmModeConnector *connector;
drmModeRes *resources;
struct weston_head *base, *next;
struct drm_head *head;
- uint32_t *connected;
int i;

resources = drmModeGetResources(b->drm.fd);
@@ -5296,63 +5258,45 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
return;
}

- connected = calloc(resources->count_connectors, sizeof(uint32_t));
- if (!connected) {
- drmModeFreeResources(resources);
- return;
- }
-
- /* collect new connects */
+ /* collect new connectors that have appeared, e.g. MST */
for (i = 0; i < resources->count_connectors; i++) {
uint32_t connector_id = resources->connectors[i];

- connector = drmModeGetConnector(b->drm.fd, connector_id);
- if (connector == NULL)
- continue;
-
- if (connector->connection != DRM_MODE_CONNECTED) {
- drmModeFreeConnector(connector);
- continue;
- }
-
- connected[i] = connector_id;
-
- if (drm_output_find_by_connector(b, connector_id)) {
- drmModeFreeConnector(connector);
- continue;
+ head = drm_head_find_by_connector(b, connector_id);
+ if (head) {
+ drm_head_update_info(head);
+ } else {
+ head = drm_head_create(b, connector_id, drm_device);
+ if (!head)
+ weston_log("DRM: failed to create head for hot-added connector %d.\n",
+ connector_id);
}
-
- create_output_for_connector(b, resources,
- connector, drm_device);
- weston_log("connector %d connected\n", connector_id);
}

+ /* Remove connectors that have disappeared. */
wl_list_for_each_safe(base, next,
&b->compositor->head_list, compositor_link) {
- bool disconnected = true;
+ bool removed = true;

head = to_drm_head(base);

- if (!head->output)
- continue;
-
for (i = 0; i < resources->count_connectors; i++) {
- if (connected[i] == head->connector_id) {
- disconnected = false;
+ if (resources->connectors[i] == head->connector_id) {
+ removed = false;
break;
}
}

- if (!disconnected)
+ if (!removed)
continue;

- weston_log("connector %d disconnected\n", head->connector_id);
- drm_output_destroy(&head->output->base);
+ weston_log("DRM: head '%s' (connector %d) disappeared.\n",
+ head->base.name, head->connector_id);
+ drm_head_destroy(head);
}

drm_backend_update_unused_outputs(b, resources);

- free(connected);
drmModeFreeResources(resources);
}

@@ -5382,7 +5326,7 @@ udev_drm_event(int fd, uint32_t mask, void *data)
event = udev_monitor_receive_device(b->udev_monitor);

if (udev_event_is_hotplug(b, event))
- update_outputs(b, event);
+ drm_backend_update_heads(b, event);

udev_device_unref(event);

@@ -5940,8 +5884,8 @@ drm_backend_create(struct weston_compositor *compositor,
goto err_sprite;
}

- if (create_outputs(b, drm_device) < 0) {
- weston_log("failed to create output for %s\n", b->drm.filename);
+ if (drm_backend_create_heads(b, drm_device) < 0) {
+ weston_log("Failed to create heads for %s\n", b->drm.filename);
goto err_udev_input;
}
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:41 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Replace the unused_connectors array by iterating through the head list
instead. A head that is not enabled (attached to an enabled output) is
basically an unused connector.

All connectors regardless of their status have a drm_head. This has the
nice effect that drm_pending_state_apply_atomic() does not need to
re-query the connector properties every time, they can be simply looked
up in the drm_head.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 679f78be..ea58ca96 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -253,8 +253,7 @@ struct drm_backend {

bool state_invalid;

- /* Connector and CRTC IDs not used by any enabled output. */
- struct wl_array unused_connectors;
+ /* CRTC IDs not used by any enabled output. */
struct wl_array unused_crtcs;

int cursors_are_broken;
@@ -2143,47 +2142,36 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
return -1;

if (b->state_invalid) {
+ struct weston_head *head_base;
+ struct drm_head *head;
uint32_t *unused;
int err;

/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
* disable all the CRTCs and connectors we aren't using. */
- wl_array_for_each(unused, &b->unused_connectors) {
- struct drm_property_info infos[WDRM_CONNECTOR__COUNT];
+ wl_list_for_each(head_base,
+ &b->compositor->head_list, compositor_link) {
struct drm_property_info *info;
- drmModeObjectProperties *props;
-
- memset(infos, 0, sizeof(infos));

- props = drmModeObjectGetProperties(b->drm.fd,
- *unused,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!props) {
- ret = -1;
+ if (weston_head_is_enabled(head_base))
continue;
- }

- drm_property_info_populate(b, connector_props, infos,
- WDRM_CONNECTOR__COUNT,
- props);
- drmModeFreeObjectProperties(props);
+ head = to_drm_head(head_base);

- info = &infos[WDRM_CONNECTOR_CRTC_ID];
- err = drmModeAtomicAddProperty(req, *unused,
+ info = &head->props_conn[WDRM_CONNECTOR_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, head->connector_id,
info->prop_id, 0);
if (err <= 0)
ret = -1;

- info = &infos[WDRM_CONNECTOR_DPMS];
+ info = &head->props_conn[WDRM_CONNECTOR_DPMS];
if (info->prop_id > 0)
- err = drmModeAtomicAddProperty(req, *unused,
+ err = drmModeAtomicAddProperty(req, head->connector_id,
info->prop_id,
DRM_MODE_DPMS_OFF);
if (err <= 0)
ret = -1;
-
- drm_property_info_free(infos, WDRM_CONNECTOR__COUNT);
}

wl_array_for_each(unused, &b->unused_crtcs) {
@@ -4846,8 +4834,6 @@ drm_output_enable(struct weston_output *base)
&output->scanout_plane->base,
&b->compositor->primary_plane);

- wl_array_remove_uint32(&b->unused_connectors, head->connector_id);
-
weston_log("Output %s, (connector %d, crtc %d)\n",
output->base.name, head->connector_id, output->crtc_id);
wl_list_for_each(m, &output->base.mode_list, link)
@@ -4872,9 +4858,7 @@ static void
drm_output_deinit(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
- struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
struct drm_backend *b = to_drm_backend(base->compositor);
- uint32_t *unused;

if (b->use_pixman)
drm_output_fini_pixman(output);
@@ -4897,9 +4881,6 @@ drm_output_deinit(struct weston_output *base)
}
}

- unused = wl_array_add(&b->unused_connectors, sizeof(*unused));
- *unused = head->connector_id;
-
drm_output_fini_crtc(output);
}

@@ -4959,7 +4940,7 @@ drm_output_disable(struct weston_output *base)
/**
* Update the list of unused connectors and CRTCs
*
- * This keeps the unused_connectors and unused_crtcs arrays up to date.
+ * This keeps the unused_crtc arrays up to date.
*
* @param b Weston backend structure
* @param resources DRM resources for this device
@@ -4969,22 +4950,6 @@ drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources)
{
int i;

- wl_array_release(&b->unused_connectors);
- wl_array_init(&b->unused_connectors);
-
- for (i = 0; i < resources->count_connectors; i++) {
- struct drm_head *head;
- uint32_t *connector_id;
-
- head = drm_head_find_by_connector(b, resources->connectors[i]);
- if (head && weston_head_is_enabled(&head->base))
- continue;
-
- connector_id = wl_array_add(&b->unused_connectors,
- sizeof(*connector_id));
- *connector_id = resources->connectors[i];
- }
-
wl_array_release(&b->unused_crtcs);
wl_array_init(&b->unused_crtcs);

@@ -5362,7 +5327,6 @@ drm_destroy(struct weston_compositor *ec)
weston_launcher_destroy(ec->launcher);

wl_array_release(&b->unused_crtcs);
- wl_array_release(&b->unused_connectors);

close(b->drm.fd);
free(b->drm.filename);
@@ -5796,7 +5760,6 @@ drm_backend_create(struct weston_compositor *compositor,
b->state_invalid = true;
b->drm.fd = -1;
wl_array_init(&b->unused_crtcs);
- wl_array_init(&b->unused_connectors);

/*
* KMS support for hardware planes cannot properly synchronize
--
2.13.6
Daniel Stone
2018-04-12 12:38:01 UTC
Permalink
Hi Pekka,
Post by Pekka Paalanen
Replace the unused_connectors array by iterating through the head list
instead. A head that is not enabled (attached to an enabled output) is
basically an unused connector.
All connectors regardless of their status have a drm_head. This has the
nice effect that drm_pending_state_apply_atomic() does not need to
re-query the connector properties every time, they can be simply looked
up in the drm_head.
\o/ Nice!
Post by Pekka Paalanen
if (b->state_invalid) {
+ struct weston_head *head_base;
+ struct drm_head *head;
uint32_t *unused;
int err;
/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
* disable all the CRTCs and connectors we aren't using. */
- wl_array_for_each(unused, &b->unused_connectors) {
- struct drm_property_info infos[WDRM_CONNECTOR__COUNT];
+ wl_list_for_each(head_base,
+ &b->compositor->head_list, compositor_link) {
wl_list_for_each(head, &b->compositor->head_list, base.compositor_link)

You can get rid of the head_base declaration as well as the cast that way.

That being said, I'm still working through and up to here I've only
these two comments. Otherwise, so far, very happy to land what's here.

Cheers,
Daniel
Pekka Paalanen
2018-04-12 12:48:51 UTC
Permalink
On Thu, 12 Apr 2018 14:38:01 +0200
Post by Daniel Stone
Hi Pekka,
Post by Pekka Paalanen
Replace the unused_connectors array by iterating through the head list
instead. A head that is not enabled (attached to an enabled output) is
basically an unused connector.
All connectors regardless of their status have a drm_head. This has the
nice effect that drm_pending_state_apply_atomic() does not need to
re-query the connector properties every time, they can be simply looked
up in the drm_head.
\o/ Nice!
Post by Pekka Paalanen
if (b->state_invalid) {
+ struct weston_head *head_base;
+ struct drm_head *head;
uint32_t *unused;
int err;
/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
* disable all the CRTCs and connectors we aren't using. */
- wl_array_for_each(unused, &b->unused_connectors) {
- struct drm_property_info infos[WDRM_CONNECTOR__COUNT];
+ wl_list_for_each(head_base,
+ &b->compositor->head_list, compositor_link) {
wl_list_for_each(head, &b->compositor->head_list, base.compositor_link)
You can get rid of the head_base declaration as well as the cast that way.
Actually I very specifically chose to not do that. I want to keep the
to_drm_head() call, because I envision in the future we will have
several different kinds of heads (virtual maybe) and to_drm_head()
might actually return NULL. Grepping for it will be easy, finding all
the wl_list_for_each(..., base.derived_field) will be hard.

I have been migrating the whole code base towards the explicit calls of
down-cast wrappers and away from the sneaky container_of() double-casts
as I go. This applies to outputs as well.


Thanks,
pq
Post by Daniel Stone
That being said, I'm still working through and up to here I've only
these two comments. Otherwise, so far, very happy to land what's here.
Cheers,
Daniel
Daniel Stone
2018-04-12 13:10:58 UTC
Permalink
Post by Pekka Paalanen
Post by Daniel Stone
Post by Pekka Paalanen
+ wl_list_for_each(head_base,
+ &b->compositor->head_list, compositor_link) {
wl_list_for_each(head, &b->compositor->head_list, base.compositor_link)
You can get rid of the head_base declaration as well as the cast that way.
Actually I very specifically chose to not do that. I want to keep the
to_drm_head() call, because I envision in the future we will have
several different kinds of heads (virtual maybe) and to_drm_head()
might actually return NULL. Grepping for it will be easy, finding all
the wl_list_for_each(..., base.derived_field) will be hard.
I have been migrating the whole code base towards the explicit calls of
down-cast wrappers and away from the sneaky container_of() double-casts
as I go. This applies to outputs as well.
Oh, I hadn't at all thought about virtual / mixed-backend / etc
outputs. Good plan; objection dropped.

Cheers,
Daniel
Pekka Paalanen
2018-02-16 14:57:43 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Fix this function to support more than one head per output.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e1feeab4..965efafd 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2072,10 +2072,10 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
uint32_t *flags)
{
struct drm_output *output = state->output;
- struct drm_head *head = to_drm_head(weston_output_get_first_head(&output->base));
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
+ struct drm_head *head;
int ret = 0;

if (state->dpms != output->state_cur->dpms)
@@ -2089,12 +2089,17 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
current_mode->blob_id);
ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
- ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID,
- output->crtc_id);
+
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID,
+ output->crtc_id);
+ }
} else {
ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
- ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
+
+ wl_list_for_each(head, &output->base.head_list, base.output_link)
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
}

if (ret != 0) {
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:42 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Fix this function to support more than one head per output.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index ea58ca96..e1feeab4 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1818,18 +1818,24 @@ static int
drm_output_apply_state_legacy(struct drm_output_state *state)
{
struct drm_output *output = state->output;
- struct drm_head *head = to_drm_head(weston_output_get_first_head(&output->base));
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
- struct drm_property_info *dpms_prop =
- &head->props_conn[WDRM_CONNECTOR_DPMS];
+ struct drm_property_info *dpms_prop;
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
struct drm_mode *mode;
+ struct drm_head *head;
+ uint32_t connectors[MAX_CLONED_CONNECTORS];
+ int n_conn = 0;
struct timespec now;
int ret = 0;

+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ assert(n_conn < MAX_CLONED_CONNECTORS);
+ connectors[n_conn++] = head->connector_id;
+ }
+
/* If disable_planes is set then assign_planes() wasn't
* called for this render, so we could still have a stale
* cursor plane set up.
@@ -1863,7 +1869,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
}

ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
- &head->connector_id, 0, NULL);
+ connectors, 0, NULL); /* XXX: NULL, 0 or connectors, n_conn ? */
if (ret)
weston_log("drmModeSetCrtc failed disabling: %m\n");

@@ -1898,7 +1904,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
0, 0,
- &head->connector_id, 1,
+ connectors, n_conn,
&mode->mode_info);
if (ret) {
weston_log("set mode failed: %m\n");
@@ -1969,14 +1975,20 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
}
}

- if (dpms_prop->prop_id && state->dpms != output->state_cur->dpms) {
- ret = drmModeConnectorSetProperty(backend->drm.fd,
- head->connector_id,
- dpms_prop->prop_id,
- state->dpms);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
+ if (state->dpms != output->state_cur->dpms) {
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ dpms_prop = &head->props_conn[WDRM_CONNECTOR_DPMS];
+ if (dpms_prop->prop_id == 0)
+ continue;
+
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+ head->connector_id,
+ dpms_prop->prop_id,
+ state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for %s\n",
+ head->base.name);
+ }
}
}
--
2.13.6
Daniel Stone
2018-04-12 12:41:24 UTC
Permalink
Post by Pekka Paalanen
@@ -1863,7 +1869,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
}
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
- &head->connector_id, 0, NULL);
+ connectors, 0, NULL); /* XXX: NULL, 0 or connectors, n_conn ? */
NULL, 0. Good catch. :)
Pekka Paalanen
2018-02-16 14:57:45 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Previously the log contained one line for EDID data and another line for
the head, and you just had to know they belong together. Make it more
obvious to read by putting both head and EDID info on the same line.

We no longer print EDID data every time it is parsed (on every hotplug
event), but only if it changes. I did take a shortcut here and use
weston_head::device_changed as the print condition which relies on the
compositor clearing it, but a failure to do so just means we print stuff
even if it didn't change.

Head info updates also print the head info and not just the EDID data.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b77c1879..d7d58cc3 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4334,10 +4334,6 @@ find_and_parse_output_edid(struct drm_head *head,
edid_blob->data,
edid_blob->length);
if (!rc) {
- weston_log("EDID data '%s', '%s', '%s'\n",
- head->edid.pnp_id,
- head->edid.monitor_name,
- head->edid.serial_number);
if (head->edid.pnp_id[0] != '\0')
*make = head->edid.pnp_id;
if (head->edid.monitor_name[0] != '\0')
@@ -5042,6 +5038,21 @@ drm_head_assign_connector_info(struct drm_head *head,
return 0;
}

+static void
+drm_head_log_info(struct drm_head *head, const char *msg)
+{
+ if (head->base.connected) {
+ weston_log("DRM: head '%s' %s, connector %d is connected, "
+ "EDID make '%s', model '%s', serial '%s'\n",
+ head->base.name, msg, head->connector_id,
+ head->base.make, head->base.model,
+ head->base.serial_number ?: "");
+ } else {
+ weston_log("DRM: head '%s' %s, connector %d is disconnected.\n",
+ head->base.name, msg, head->connector_id);
+ }
+}
+
/** Update connector and monitor information
*
* @param head The head to update.
@@ -5065,6 +5076,9 @@ drm_head_update_info(struct drm_head *head)

if (drm_head_assign_connector_info(head, connector) < 0)
drmModeFreeConnector(connector);
+
+ if (head->base.device_changed)
+ drm_head_log_info(head, "updated");
}

/**
@@ -5121,10 +5135,7 @@ drm_head_create(struct drm_backend *backend, uint32_t connector_id,
}

weston_compositor_add_head(backend->compositor, &head->base);
-
- weston_log("DRM: found head '%s', connector %d %s.\n",
- head->base.name, head->connector_id,
- head->base.connected ? "connected" : "disconnected");
+ drm_head_log_info(head, "found");

return head;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:44 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Fix this function to support more than one head per output.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 965efafd..b77c1879 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3824,21 +3824,24 @@ drm_get_backlight(struct drm_head *head)
static void
drm_set_backlight(struct weston_output *output_base, uint32_t value)
{
- struct drm_head *head = to_drm_head(weston_output_get_first_head(output_base));
+ struct drm_output *output = to_drm_output(output_base);
+ struct drm_head *head;
long max_brightness, new_brightness;

- if (!head->backlight)
- return;
-
if (value > 255)
return;

- max_brightness = backlight_get_max_brightness(head->backlight);
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ if (!head->backlight)
+ return;

- /* get denormalized value */
- new_brightness = (value * max_brightness) / 255;
+ max_brightness = backlight_get_max_brightness(head->backlight);

- backlight_set_brightness(head->backlight, new_brightness);
+ /* get denormalized value */
+ new_brightness = (value * max_brightness) / 255;
+
+ backlight_set_brightness(head->backlight, new_brightness);
+ }
}

/**
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:47 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

If an output has multiple (cloned) heads, it should be enough for any
head to support backlight control for DRM-backend to expose it.

Inspect all attached heads for backlight control and improve the
logging.

Pick the initial backlight level from whatever happens to be the "first"
head, because it's simple.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 63ce8e58..a6cfffda 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3844,6 +3844,35 @@ drm_set_backlight(struct weston_output *output_base, uint32_t value)
}
}

+static void
+drm_output_init_backlight(struct drm_output *output)
+{
+ struct weston_head *base;
+ struct drm_head *head;
+
+ output->base.set_backlight = NULL;
+
+ wl_list_for_each(base, &output->base.head_list, output_link) {
+ head = to_drm_head(base);
+
+ if (head->backlight) {
+ weston_log("Initialized backlight for head '%s', device %s\n",
+ head->base.name, head->backlight->path);
+
+ if (!output->base.set_backlight) {
+ output->base.set_backlight = drm_set_backlight;
+ output->base.backlight_current =
+ drm_get_backlight(head);
+ }
+ }
+ }
+
+ if (!output->base.set_backlight) {
+ weston_log("No backlight control for output '%s'\n",
+ output->base.name);
+ }
+}
+
/**
* Power output on or off
*
@@ -4917,14 +4946,7 @@ drm_output_enable(struct weston_output *base)
goto err;
}

- if (head->backlight) {
- weston_log("Initialized backlight, device %s\n",
- head->backlight->path);
- output->base.set_backlight = drm_set_backlight;
- output->base.backlight_current = drm_get_backlight(head);
- } else {
- weston_log("Failed to initialize backlight\n");
- }
+ drm_output_init_backlight(output);

output->base.start_repaint_loop = drm_output_start_repaint_loop;
output->base.repaint = drm_output_repaint;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:46 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

A single list of modes needs to be combined from the mode lists in each
attached head. We could just concatenate the lists, but that might
introduce duplicates. Try to avoid duplicates instead by using partially
fuzzy matching.

When a duplicate is found, try to figure out which is more suitable to
use in place of both. If one has the preferred flag and the other
doesn't, take the preferred one. Otherwise use the one already in the
list.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d7d58cc3..63ce8e58 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4446,6 +4446,101 @@ parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
return ret;
}

+static uint32_t
+u32distance(uint32_t a, uint32_t b)
+{
+ if (a < b)
+ return b - a;
+ else
+ return a - b;
+}
+
+/** Choose equivalent mode
+ *
+ * If the two modes are not equivalent, return NULL.
+ * Otherwise return the mode that is more likely to work in place of both.
+ *
+ * None of the fuzzy matching criteria in this function have any justification.
+ *
+ * typedef struct _drmModeModeInfo {
+ * uint32_t clock;
+ * uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+ * uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+ *
+ * uint32_t vrefresh;
+ *
+ * uint32_t flags;
+ * uint32_t type;
+ * char name[DRM_DISPLAY_MODE_LEN];
+ * } drmModeModeInfo, *drmModeModeInfoPtr;
+ */
+static const drmModeModeInfo *
+drm_mode_pick_equivalent(const drmModeModeInfo *a, const drmModeModeInfo *b)
+{
+ uint32_t refresh_a, refresh_b;
+
+ if (a->hdisplay != b->hdisplay || a->vdisplay != b->vdisplay)
+ return NULL;
+
+ if (a->flags != b->flags)
+ return NULL;
+
+ /* kHz */
+ if (u32distance(a->clock, b->clock) > 500)
+ return NULL;
+
+ refresh_a = drm_refresh_rate_mHz(a);
+ refresh_b = drm_refresh_rate_mHz(b);
+ if (u32distance(refresh_a, refresh_b) > 50)
+ return NULL;
+
+ if ((a->type ^ b->type) & DRM_MODE_TYPE_PREFERRED) {
+ if (a->type & DRM_MODE_TYPE_PREFERRED)
+ return a;
+ else
+ return b;
+ }
+
+ return a;
+}
+
+/* If the given mode info is not already in the list, add it.
+ * If it is in the list, either keep the existing or replace it,
+ * depending on which one is "better".
+ */
+static int
+drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
+{
+ struct weston_mode *base;
+ struct drm_mode *mode;
+ struct drm_backend *backend;
+ const drmModeModeInfo *chosen = NULL;
+
+ assert(info);
+
+ wl_list_for_each(base, &output->base.mode_list, link) {
+ mode = to_drm_mode(base);
+ chosen = drm_mode_pick_equivalent(&mode->mode_info, info);
+ if (chosen)
+ break;
+ }
+
+ if (chosen == info) {
+ backend = to_drm_backend(output->base.compositor);
+ drm_output_destroy_mode(backend, mode);
+ chosen = NULL;
+ }
+
+ if (!chosen) {
+ mode = drm_output_add_mode(output, info);
+ if (!mode)
+ return -1;
+ }
+ /* else { the equivalent mode is already in the list } */
+
+ return 0;
+}
+
/** Rewrite the output's mode list
*
* @param output The output.
@@ -4462,22 +4557,21 @@ drm_output_update_modelist_from_heads(struct drm_output *output)
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct weston_head *head_base;
struct drm_head *head;
- struct drm_mode *mode;
int i;
+ int ret;

assert(!output->base.enabled);

drm_mode_list_destroy(backend, &output->base.mode_list);

- /* XXX: needs a strategy for combining mode lists from multiple heads */
- head_base = weston_output_get_first_head(&output->base);
- assert(head_base);
- head = to_drm_head(head_base);
-
- for (i = 0; i < head->connector->count_modes; i++) {
- mode = drm_output_add_mode(output, &head->connector->modes[i]);
- if (!mode)
- return -1;
+ wl_list_for_each(head_base, &output->base.head_list, output_link) {
+ head = to_drm_head(head_base);
+ for (i = 0; i < head->connector->count_modes; i++) {
+ ret = drm_output_try_add_mode(output,
+ &head->connector->modes[i]);
+ if (ret < 0)
+ return -1;
+ }
}

return 0;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:48 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Stop using a head for printing the mode list, because there could be
multiple heads. We already gather the mode list from all heads.

No need to print the connector id here, because it is logged with DRM
heads, and core prints the head names on output enable.

The "built-in" flag seemed dead, because it could only be printed if the
kernel provided no modes. If we want more detailed info on where modes
come from, we would need to inspect mode_info or add new flags to
drm_mode or weston_mode.

Add printing the pixel clock, because that is used by the video mode
duplicate removal code.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a6cfffda..d470647c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4910,13 +4910,31 @@ drm_output_fini_crtc(struct drm_output *output)
output->scanout_plane = NULL;
}

+static void
+drm_output_print_modes(struct drm_output *output)
+{
+ struct weston_mode *m;
+ struct drm_mode *dm;
+
+ wl_list_for_each(m, &output->base.mode_list, link) {
+ dm = to_drm_mode(m);
+
+ weston_log_continue(STAMP_SPACE "%dx%d@%.1f%s%s, %.1f MHz\n",
+ m->width, m->height, m->refresh / 1000.0,
+ m->flags & WL_OUTPUT_MODE_PREFERRED ?
+ ", preferred" : "",
+ m->flags & WL_OUTPUT_MODE_CURRENT ?
+ ", current" : "",
+ dm->mode_info.clock / 1000.0);
+ }
+}
+
static int
drm_output_enable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
- struct weston_mode *m;
drmModeRes *resources;
int ret;

@@ -4966,17 +4984,9 @@ drm_output_enable(struct weston_output *base)
&output->scanout_plane->base,
&b->compositor->primary_plane);

- weston_log("Output %s, (connector %d, crtc %d)\n",
- output->base.name, head->connector_id, output->crtc_id);
- wl_list_for_each(m, &output->base.mode_list, link)
- weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f%s%s%s\n",
- m->width, m->height, m->refresh / 1000.0,
- m->flags & WL_OUTPUT_MODE_PREFERRED ?
- ", preferred" : "",
- m->flags & WL_OUTPUT_MODE_CURRENT ?
- ", current" : "",
- head->connector->count_modes == 0 ?
- ", built-in" : "");
+ weston_log("Output %s (crtc %d) video modes:\n",
+ output->base.name, output->crtc_id);
+ drm_output_print_modes(output);

return 0;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:50 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

The head was just zalloc()'d, there is no need to memset it to zero.

If a function fails, it is preferable it leaves the output arguments
untouched.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 352bd17b..8dfe2baf 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4698,7 +4698,6 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
static int
drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend)
{
- drmModeModeInfo *mode = &head->inherited_mode;
int drm_fd = backend->drm.fd;
drmModeEncoder *encoder;
drmModeCrtc *crtc;
@@ -4706,14 +4705,13 @@ drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend)
/* Get the current mode on the crtc that's currently driving
* this connector. */
encoder = drmModeGetEncoder(drm_fd, head->connector->encoder_id);
- memset(mode, 0, sizeof *mode);
if (encoder != NULL) {
crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
drmModeFreeEncoder(encoder);
if (crtc == NULL)
return -1;
if (crtc->mode_valid)
- *mode = crtc->mode;
+ head->inherited_mode = crtc->mode;
drmModeFreeCrtc(crtc);
}

@@ -5257,7 +5255,7 @@ drm_head_create(struct drm_backend *backend, uint32_t connector_id,
if (drm_head_read_current_setup(head, backend) < 0) {
weston_log("Failed to retrieve current mode from connector %d.\n",
head->connector_id);
- /* Continue, inherited_mode was memset to zero. */
+ /* Not fatal. */
}

weston_compositor_add_head(backend->compositor, &head->base);
--
2.13.6
Daniel Stone
2018-04-12 13:19:33 UTC
Permalink
Hi Pekka,
Post by Pekka Paalanen
The head was just zalloc()'d, there is no need to memset it to zero.
If a function fails, it is preferable it leaves the output arguments
untouched.
Everything to this point is:
Reviewed-by: Daniel Stone <***@collabora.com>

and I'm happy for you to merge. Given I'm away today and tomorrow, I
won't be able to properly test multi-head and hotplugging until next
week, so if you want to go ahead and land it, I'll rebase the
remainder of the atomic series on top of that, fix the inevitable
bugs, and send it out.

I'm really happy with how this series has unfolded: there were a
couple of small places where things seemed mis-sequenced, but on the
whole it was very easy to read through and find a coherent story. Much
less scary than I feared. Thanks!

Cheers,
Daniel
Pekka Paalanen
2018-02-16 14:57:51 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

To support shared-CRTC clone mode, the chosen CRTC needs to support
driving all the attached connectors. Replace the old algorithm with a
new one that takes into account all associated connectors.

Ideally it should use possible_clones mask to check which encoders (and
therefore connectors) actually can be in a cloned set. However, the DRM
documentation says about possible_clones and possible_crtcs masks both:
"In reality almost every driver gets this wrong."
- https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html#c.drm_encoder

Looking at a target device and its kernel where clone mode is desired,
possible_clones is indeed self-conflicting and would not allow cloning
at all. Therefore the implemented algorithm replaces the checking of
possible_clones with luck. It even goes out of its way to find any CRTC
for a configuration, even if not advertised by the kernel as not
supported.

Libweston would need infrastructure to allow trial-and-error CRTC
allocation: rather than picking one CRTC in advance and do or die, it
should try all available CRTCs one by one. Unfortunately that is not yet
possible, so this patch implements what it can. It is also the DRM
upstream opinion that trial-and-error with ATOMIC_TEST would be the way
to go.

Unlike the old algorithm, the new algorithm prefers routings that were
in place when Weston started instead of when enabling an output. When
you never temporarily disable an output, this makes no difference.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8dfe2baf..f1270390 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -416,6 +416,7 @@ struct drm_head {
struct backlight *backlight;

drmModeModeInfo inherited_mode; /**< Original mode on the connector */
+ uint32_t inherited_crtc_id; /**< Original CRTC assignment */
};

struct drm_output {
@@ -3991,51 +3992,6 @@ make_connector_name(const drmModeConnector *con)
return name;
}

-static int
-find_crtc_for_connector(struct drm_backend *b,
- drmModeRes *resources, drmModeConnector *connector)
-{
- drmModeEncoder *encoder;
- int i, j;
- int ret = -1;
-
- for (j = 0; j < connector->count_encoders; j++) {
- uint32_t possible_crtcs, encoder_id, crtc_id;
-
- encoder = drmModeGetEncoder(b->drm.fd, connector->encoders[j]);
- if (encoder == NULL) {
- weston_log("Failed to get encoder.\n");
- continue;
- }
- encoder_id = encoder->encoder_id;
- possible_crtcs = encoder->possible_crtcs;
- crtc_id = encoder->crtc_id;
- drmModeFreeEncoder(encoder);
-
- for (i = 0; i < resources->count_crtcs; i++) {
- if (!(possible_crtcs & (1 << i)))
- continue;
-
- if (drm_output_find_by_crtc(b, resources->crtcs[i]))
- continue;
-
- /* Try to preserve the existing
- * CRTC -> encoder -> connector routing; it makes
- * initialisation faster, and also since we have a
- * very dumb picking algorithm, may preserve a better
- * choice. */
- if (!connector->encoder_id ||
- (encoder_id == connector->encoder_id &&
- crtc_id == resources->crtcs[i]))
- return i;
-
- ret = i;
- }
- }
-
- return ret;
-}
-
static void drm_output_fini_cursor_egl(struct drm_output *output)
{
unsigned int i;
@@ -4706,8 +4662,11 @@ drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend)
* this connector. */
encoder = drmModeGetEncoder(drm_fd, head->connector->encoder_id);
if (encoder != NULL) {
+ head->inherited_crtc_id = encoder->crtc_id;
+
crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
drmModeFreeEncoder(encoder);
+
if (crtc == NULL)
return -1;
if (crtc->mode_valid)
@@ -4794,14 +4753,134 @@ drm_output_init_gamma_size(struct drm_output *output)
return 0;
}

+static uint32_t
+drm_head_get_possible_crtcs_mask(struct drm_head *head)
+{
+ uint32_t possible_crtcs = 0;
+ drmModeEncoder *encoder;
+ int i;
+
+ for (i = 0; i < head->connector->count_encoders; i++) {
+ encoder = drmModeGetEncoder(head->backend->drm.fd,
+ head->connector->encoders[i]);
+ if (!encoder)
+ continue;
+
+ possible_crtcs |= encoder->possible_crtcs;
+ drmModeFreeEncoder(encoder);
+ }
+
+ return possible_crtcs;
+}
+
+static int
+drm_crtc_get_index(drmModeRes *resources, uint32_t crtc_id)
+{
+ int i;
+
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (resources->crtcs[i] == crtc_id)
+ return i;
+ }
+
+ assert(0 && "unknown crtc id");
+ return -1;
+}
+
+/** Pick a CRTC that might be able to drive all attached connectors
+ *
+ * @param output The output whose attached heads to include.
+ * @param resources The DRM KMS resources.
+ * @return CRTC index, or -1 on failure or not found.
+ */
+static int
+drm_output_pick_crtc(struct drm_output *output, drmModeRes *resources)
+{
+ struct drm_backend *backend;
+ struct weston_head *base;
+ struct drm_head *head;
+ uint32_t possible_crtcs = 0xffffffff;
+ int existing_crtc[32];
+ unsigned j, n = 0;
+ uint32_t crtc_id;
+ int best_crtc_index = -1;
+ int i;
+
+ backend = to_drm_backend(output->base.compositor);
+
+ /* This algorithm ignores drmModeEncoder::possible_clones restriction,
+ * because it is more often set wrong than not in the kernel. */
+
+ /* Accumulate a mask of possible crtcs and find existing routings. */
+ wl_list_for_each(base, &output->base.head_list, output_link) {
+ head = to_drm_head(base);
+
+ possible_crtcs &= drm_head_get_possible_crtcs_mask(head);
+
+ crtc_id = head->inherited_crtc_id;
+ if (crtc_id > 0 && n < ARRAY_LENGTH(existing_crtc))
+ existing_crtc[n++] = drm_crtc_get_index(resources,
+ crtc_id);
+ }
+
+ /* Find a crtc that could drive each connector individually at least,
+ * and prefer existing routings. */
+ for (i = 0; i < resources->count_crtcs; i++) {
+ crtc_id = resources->crtcs[i];
+
+ /* Could the crtc not drive each connector? */
+ if (!(possible_crtcs & (1 << i)))
+ continue;
+
+ /* Is the crtc already in use? */
+ if (drm_output_find_by_crtc(backend, crtc_id))
+ continue;
+
+ /* Try to preserve the existing CRTC -> connector routing;
+ * it makes initialisation faster, and also since we have a
+ * very dumb picking algorithm, may preserve a better
+ * choice. */
+ for (j = 0; j < n; j++) {
+ if (existing_crtc[j] == i)
+ return i;
+ }
+
+ best_crtc_index = i;
+ }
+
+ if (best_crtc_index != -1)
+ return best_crtc_index;
+
+ /* Likely possible_crtcs was empty due to asking for clones,
+ * but since the DRM documentation says the kernel lies, let's
+ * pick one crtc anyway. Trial and error is the only way to
+ * be sure if something doesn't work. */
+
+ /* First pick any existing assignment. */
+ for (j = 0; j < n; j++) {
+ crtc_id = resources->crtcs[existing_crtc[j]];
+ if (!drm_output_find_by_crtc(backend, crtc_id))
+ return existing_crtc[j];
+ }
+
+ /* Otherwise pick any available crtc. */
+ for (i = 0; i < resources->count_crtcs; i++) {
+ crtc_id = resources->crtcs[i];
+
+ if (!drm_output_find_by_crtc(backend, crtc_id))
+ return i;
+ }
+
+ return -1;
+}
+
/** Allocate a CRTC for the output
*
* @param output The output with no allocated CRTC.
* @param resources DRM KMS resources.
- * @param connector The DRM KMS connector data.
* @return 0 on success, -1 on failure.
*
- * Finds a free CRTC that can drive the given connector, reserves the CRTC
+ * Finds a free CRTC that might drive the attached connectors, reserves the CRTC
* for the output, and loads the CRTC properties.
*
* Populates the cursor and scanout planes.
@@ -4809,8 +4888,7 @@ drm_output_init_gamma_size(struct drm_output *output)
* On failure, the output remains without a CRTC.
*/
static int
-drm_output_init_crtc(struct drm_output *output,
- drmModeRes *resources, drmModeConnector *connector)
+drm_output_init_crtc(struct drm_output *output, drmModeRes *resources)
{
struct drm_backend *b = to_drm_backend(output->base.compositor);
drmModeObjectPropertiesPtr props;
@@ -4818,9 +4896,10 @@ drm_output_init_crtc(struct drm_output *output,

assert(output->crtc_id == 0);

- i = find_crtc_for_connector(b, resources, connector);
+ i = drm_output_pick_crtc(output, resources);
if (i < 0) {
- weston_log("No usable crtc/encoder pair for connector.\n");
+ weston_log("Output '%s': No available CRTCs.\n",
+ output->base.name);
return -1;
}

@@ -4933,7 +5012,6 @@ drm_output_enable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
drmModeRes *resources;
int ret;

@@ -4942,7 +5020,7 @@ drm_output_enable(struct weston_output *base)
weston_log("drmModeGetResources failed\n");
return -1;
}
- ret = drm_output_init_crtc(output, resources, head->connector);
+ ret = drm_output_init_crtc(output, resources);
drmModeFreeResources(resources);
if (ret < 0)
return -1;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:49 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Rename connector_get_current_mode() because it will be useful for
storing not just the current mode on creating a head.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d470647c..352bd17b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4696,15 +4696,16 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
}

static int
-connector_get_current_mode(drmModeConnector *connector, int drm_fd,
- drmModeModeInfo *mode)
+drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend)
{
+ drmModeModeInfo *mode = &head->inherited_mode;
+ int drm_fd = backend->drm.fd;
drmModeEncoder *encoder;
drmModeCrtc *crtc;

/* Get the current mode on the crtc that's currently driving
* this connector. */
- encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
+ encoder = drmModeGetEncoder(drm_fd, head->connector->encoder_id);
memset(mode, 0, sizeof *mode);
if (encoder != NULL) {
crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
@@ -5253,8 +5254,7 @@ drm_head_create(struct drm_backend *backend, uint32_t connector_id,
head->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
weston_head_set_internal(&head->base);

- if (connector_get_current_mode(head->connector, backend->drm.fd,
- &head->inherited_mode) < 0) {
+ if (drm_head_read_current_setup(head, backend) < 0) {
weston_log("Failed to retrieve current mode from connector %d.\n",
head->connector_id);
/* Continue, inherited_mode was memset to zero. */
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:53 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

When a head is detached from an enabled output, that output needs to go
through a modeset (drmModeSetCrtc() / ATOMIC_ALLOW_MODESET) so that the
connector is actually removed from the CRTC.

This has not yet been a problem, because an output could only have one
head at a time, and would be automatically disabled on detach. It would
be a problem with clone mode.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 82f014d7..d1fc66ac 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4410,6 +4410,23 @@ drm_output_attach_head(struct weston_output *output_base,
return 0;
}

+static void
+drm_output_detach_head(struct weston_output *output_base,
+ struct weston_head *head_base)
+{
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+
+ if (!output_base->enabled)
+ return;
+
+ /* Need to go through modeset to drop connectors that should no longer
+ * be driven. */
+ /* XXX: Ideally we'd do this per-output, not globally. */
+ b->state_invalid = true;
+
+ weston_output_schedule_repaint(output_base);
+}
+
static int
parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
{
@@ -5423,6 +5440,7 @@ drm_output_create(struct weston_compositor *compositor, const char *name)
output->base.destroy = drm_output_destroy;
output->base.disable = drm_output_disable;
output->base.attach_head = drm_output_attach_head;
+ output->base.detach_head = drm_output_detach_head;

output->destroy_pending = 0;
output->disable_pending = 0;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:52 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

If we are processing a connector that does not have an existing routing,
it is possible we pick a CRTC that was previously routed to a connector
we have not enabled yet. If that happens, the latter connector cannot
preserve its routing.

Check that no other connector we might enable later had this CRTC
before.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index f1270390..82f014d7 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4804,7 +4804,9 @@ drm_output_pick_crtc(struct drm_output *output, drmModeRes *resources)
unsigned j, n = 0;
uint32_t crtc_id;
int best_crtc_index = -1;
+ int fallback_crtc_index = -1;
int i;
+ bool match;

backend = to_drm_backend(output->base.compositor);

@@ -4845,12 +4847,37 @@ drm_output_pick_crtc(struct drm_output *output, drmModeRes *resources)
return i;
}

- best_crtc_index = i;
+ /* Check if any other head had existing routing to this CRTC.
+ * If they did, this is not the best CRTC as it might be needed
+ * for another output we haven't enabled yet. */
+ match = false;
+ wl_list_for_each(base, &backend->compositor->head_list,
+ compositor_link) {
+ head = to_drm_head(base);
+
+ if (head->base.output == &output->base)
+ continue;
+
+ if (weston_head_is_enabled(&head->base))
+ continue;
+
+ if (head->inherited_crtc_id == crtc_id) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ best_crtc_index = i;
+
+ fallback_crtc_index = i;
}

if (best_crtc_index != -1)
return best_crtc_index;

+ if (fallback_crtc_index != -1)
+ return fallback_crtc_index;
+
/* Likely possible_crtcs was empty due to asking for clones,
* but since the DRM documentation says the kernel lies, let's
* pick one crtc anyway. Trial and error is the only way to
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:54 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

For the attach on an enabled output to have an effect, we need to go
through drmModeSetCrtc or ATOMIC_ALLOW_MODESET.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d1fc66ac..d0196715 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4404,9 +4404,24 @@ static int
drm_output_attach_head(struct weston_output *output_base,
struct weston_head *head_base)
{
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+
if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS)
return -1;

+ if (!output_base->enabled)
+ return 0;
+
+ /* XXX: ensure the configuration will work.
+ * This is actually impossible without major infrastructure
+ * work. */
+
+ /* Need to go through modeset to add connectors. */
+ /* XXX: Ideally we'd do this per-output, not globally. */
+ b->state_invalid = true;
+
+ weston_output_schedule_repaint(output_base);
+
return 0;
}
--
2.13.6
Daniel Stone
2018-04-12 12:55:01 UTC
Permalink
Post by Pekka Paalanen
For the attach on an enabled output to have an effect, we need to go
through drmModeSetCrtc or ATOMIC_ALLOW_MODESET.
---
libweston/compositor-drm.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d1fc66ac..d0196715 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4404,9 +4404,24 @@ static int
drm_output_attach_head(struct weston_output *output_base,
struct weston_head *head_base)
{
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+
if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS)
return -1;
+ if (!output_base->enabled)
+ return 0;
+
+ /* XXX: ensure the configuration will work.
+ * This is actually impossible without major infrastructure
+ * work. */
+
+ /* Need to go through modeset to add connectors. */
+ /* XXX: Ideally we'd do this per-output, not globally. */
+ b->state_invalid = true;
How about just an output->heads_dirty flag or something, which would
ensure ALLOW_MODESET/SetCrtc are called?

Cheers,
Daniel
Pekka Paalanen
2018-02-16 14:57:55 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Allow cloning up to 4 connectors from the same CRTC. All the
implementation bits support more than one head per output already.

Four is just an arbitary number, small but unlikely to ever be the
limiting factor in cloning since hardware is usually very restricted.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d0196715..a6cc39b6 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -83,7 +83,7 @@
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif

-#define MAX_CLONED_CONNECTORS 1
+#define MAX_CLONED_CONNECTORS 4

/**
* Represents the values of an enum-type KMS property
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:57 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Rename user_data to wet, because it is called wet everywhere else.

Drop the local variable ec, because that is available as wet.compositor.

This models a little better that wet_compositor owns weston_compositor,
and not the other way around.

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

diff --git a/compositor/main.c b/compositor/main.c
index bbd00bb1..e549801e 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1804,7 +1804,6 @@ int main(int argc, char *argv[])
int ret = EXIT_FAILURE;
char *cmdline;
struct wl_display *display;
- struct weston_compositor *ec;
struct wl_event_source *signals[4];
struct wl_event_loop *loop;
int i, fd;
@@ -1827,7 +1826,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 = { 0 };
+ struct wet_compositor wet = { 0 };
int require_input;
int32_t wait_for_debugger = 0;

@@ -1900,8 +1899,8 @@ int main(int argc, char *argv[])

if (load_configuration(&config, noconfig, config_file) < 0)
goto out_signals;
- user_data.config = config;
- user_data.parsed_options = NULL;
+ wet.config = config;
+ wet.parsed_options = NULL;

section = weston_config_get_section(config, "core", NULL, NULL);

@@ -1922,28 +1921,27 @@ int main(int argc, char *argv[])
backend = weston_choose_default_backend();
}

- ec = weston_compositor_create(display, &user_data);
- user_data.compositor = ec;
- if (ec == NULL) {
+ wet.compositor = weston_compositor_create(display, &wet);
+ if (wet.compositor == NULL) {
weston_log("fatal: failed to create compositor\n");
goto out;
}
- segv_compositor = ec;
+ segv_compositor = wet.compositor;

- if (weston_compositor_init_config(ec, config) < 0)
+ if (weston_compositor_init_config(wet.compositor, config) < 0)
goto out;

weston_config_section_get_bool(section, "require-input",
&require_input, true);
- ec->require_input = require_input;
+ wet.compositor->require_input = require_input;

- if (load_backend(ec, backend, &argc, argv, config) < 0) {
+ if (load_backend(wet.compositor, backend, &argc, argv, config) < 0) {
weston_log("fatal: failed to create compositor backend\n");
goto out;
}

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

if (idle_time < 0)
@@ -1951,11 +1949,11 @@ int main(int argc, char *argv[])
if (idle_time < 0)
idle_time = 300; /* default idle timeout, in seconds */

- ec->idle_time = idle_time;
- ec->default_pointer_grab = NULL;
- ec->exit = handle_exit;
+ wet.compositor->idle_time = idle_time;
+ wet.compositor->default_pointer_grab = NULL;
+ wet.compositor->exit = handle_exit;

- weston_compositor_log_capabilities(ec);
+ weston_compositor_log_capabilities(wet.compositor);

server_socket = getenv("WAYLAND_SERVER_SOCKET");
if (server_socket) {
@@ -1984,28 +1982,28 @@ int main(int argc, char *argv[])
weston_config_section_get_string(section, "shell", &shell,
"desktop-shell.so");

- if (wet_load_shell(ec, shell, &argc, argv) < 0)
+ if (wet_load_shell(wet.compositor, shell, &argc, argv) < 0)
goto out;

weston_config_section_get_string(section, "modules", &modules, "");
- if (load_modules(ec, modules, &argc, argv, &xwayland) < 0)
+ if (load_modules(wet.compositor, modules, &argc, argv, &xwayland) < 0)
goto out;

- if (load_modules(ec, option_modules, &argc, argv, &xwayland) < 0)
+ if (load_modules(wet.compositor, option_modules, &argc, argv, &xwayland) < 0)
goto out;

if (!xwayland)
weston_config_section_get_bool(section, "xwayland", &xwayland,
false);
if (xwayland) {
- if (wet_load_xwayland(ec) < 0)
+ if (wet_load_xwayland(wet.compositor) < 0)
goto out;
}

section = weston_config_get_section(config, "keyboard", NULL, NULL);
weston_config_section_get_bool(section, "numlock-on", &numlock_on, 0);
if (numlock_on) {
- wl_list_for_each(seat, &ec->seat_list, link) {
+ wl_list_for_each(seat, &wet.compositor->seat_list, link) {
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(seat);

@@ -2021,7 +2019,7 @@ int main(int argc, char *argv[])
if (argc > 1)
goto out;

- weston_compositor_wake(ec);
+ weston_compositor_wake(wet.compositor);

wl_display_run(display);

@@ -2031,13 +2029,13 @@ int main(int argc, char *argv[])
* that want to indicate failure status to
* testing infrastructure above
*/
- ret = ec->exit_code;
+ ret = wet.compositor->exit_code;

out:
/* free(NULL) is valid, and it won't be NULL if it's used */
- free(user_data.parsed_options);
+ free(wet.parsed_options);

- weston_compositor_destroy(ec);
+ weston_compositor_destroy(wet.compositor);

out_signals:
for (i = ARRAY_LENGTH(signals) - 1; i >= 0; i--)
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:56 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

This makes it easier to just pass wet_compositor around and take the
weston_compositor from it.

It feels weird to go from weston_compositor to wet_compositor all the
time in internal functions. It's necessary in callbacks that cannot
carry wet_compositor, but otherwise it is awkward.

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

diff --git a/compositor/main.c b/compositor/main.c
index c9554625..bbd00bb1 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -76,6 +76,7 @@ struct wet_head_tracker {
};

struct wet_compositor {
+ struct weston_compositor *compositor;
struct weston_config *config;
struct wet_output_config *parsed_options;
bool drm_use_current_mode;
@@ -1085,13 +1086,13 @@ wet_head_tracker_create(struct wet_compositor *compositor,
}

static void
-simple_head_enable(struct weston_compositor *compositor, struct weston_head *head)
+simple_head_enable(struct wet_compositor *wet, 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);
+ output = weston_compositor_create_output_with_head(wet->compositor,
+ head);
if (!output) {
weston_log("Could not create an output for head \"%s\".\n",
weston_head_get_name(head));
@@ -1147,18 +1148,19 @@ simple_head_disable(struct weston_head *head)
static void
simple_heads_changed(struct weston_compositor *compositor)
{
+ struct wet_compositor *wet = to_wet_compositor(compositor);
struct weston_head *head = NULL;
bool connected;
bool enabled;
bool changed;

- while ((head = weston_compositor_iterate_heads(compositor, head))) {
+ while ((head = weston_compositor_iterate_heads(wet->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);
+ simple_head_enable(wet, head);
} else if (!connected && enabled) {
simple_head_disable(head);
} else if (enabled && changed) {
@@ -1921,6 +1923,7 @@ int main(int argc, char *argv[])
}

ec = weston_compositor_create(display, &user_data);
+ user_data.compositor = ec;
if (ec == NULL) {
weston_log("fatal: failed to create compositor\n");
goto out;
--
2.13.6
Pekka Paalanen
2018-02-16 14:57:58 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Add a new output section key "same-as" for configuring clone mode. An
output marked "same-as" another output will be configured identically to
the other output.

The current implementation supports only CRTC sharing for clone mode.
Independent CRTC clone mode cannot be supported until output layout
logic is moved from libweston into the frontend and libweston's damage
tracking issues stemming from overlapping outputs are solved.

Quite a lot of infrastructure is needed to properly configure clone
mode. The implemented logic allows easy addition of independent CRTC
clone mode once libweston supports it. The idea is that wet_layoutput is
the item to be laid out and all weston_outputs a wet_layoutput
contains show exactly the same area of the desktop.

The configuration logic attempts to automatically fall back to creating
more weston_outputs when all heads do not work under the same
weston_output. For now, the fallback path ends with an error message.

Enabling a weston_output is bit complicated, because one needs to first
collect all relevant heads, try to attach them all to the weston_output,
and then back up head by head until enabling the weston_output succeeds.
A new weston_output is created for the left-over heads and the process
is repeated.

CRTC-sharing clone mode is the most efficient clone mode, offering
synchronized scanout timings, but it is not always supported by
hardware.

v6:
- Add man-page note about cms-colord.
- Don't create an output just to turn it off.

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
---
compositor/main.c | 483 +++++++++++++++++++++++++++++++++++++++++++++++++++--
man/weston-drm.man | 12 ++
2 files changed, 481 insertions(+), 14 deletions(-)

diff --git a/compositor/main.c b/compositor/main.c
index e549801e..6650676a 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -70,11 +70,41 @@ struct wet_output_config {
};

struct wet_compositor;
+struct wet_layoutput;

struct wet_head_tracker {
struct wl_listener head_destroy_listener;
};

+/** User data for each weston_output */
+struct wet_output {
+ struct weston_output *output;
+ struct wl_listener output_destroy_listener;
+ struct wet_layoutput *layoutput;
+ struct wl_list link; /**< in wet_layoutput::output_list */
+};
+
+#define MAX_CLONE_HEADS 16
+
+struct wet_head_array {
+ struct weston_head *heads[MAX_CLONE_HEADS]; /**< heads to add */
+ unsigned n; /**< the number of heads */
+};
+
+/** A layout output
+ *
+ * Contains wet_outputs that are all clones (independent CRTCs).
+ * Stores output layout information in the future.
+ */
+struct wet_layoutput {
+ struct wet_compositor *compositor;
+ struct wl_list compositor_link; /**< in wet_compositor::layoutput_list */
+ struct wl_list output_list; /**< wet_output::link */
+ char *name;
+ struct weston_config_section *section;
+ struct wet_head_array add; /**< tmp: heads to add as clones */
+};
+
struct wet_compositor {
struct weston_compositor *compositor;
struct weston_config *config;
@@ -82,6 +112,7 @@ struct wet_compositor {
bool drm_use_current_mode;
int (*simple_output_configure)(struct weston_output *output);
bool init_failed;
+ struct wl_list layoutput_list; /**< wet_layoutput::compositor_link */
};

static FILE *weston_logfile = NULL;
@@ -1112,10 +1143,6 @@ simple_head_enable(struct wet_compositor *wet, struct weston_head *head)
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));
@@ -1208,32 +1235,29 @@ configure_input_device(struct weston_compositor *compositor,
}

static int
-drm_backend_output_configure(struct weston_output *output)
+drm_backend_output_configure(struct weston_output *output,
+ struct weston_config_section *section)
{
- struct weston_config *wc = wet_get_config(output->compositor);
struct wet_compositor *wet = to_wet_compositor(output->compositor);
- struct weston_config_section *section;
- const struct weston_drm_output_api *api = weston_drm_output_get_api(output->compositor);
+ const struct weston_drm_output_api *api;
enum weston_drm_backend_output_mode mode =
WESTON_DRM_BACKEND_OUTPUT_PREFERRED;
-
char *s;
char *modeline = NULL;
char *gbm_format = NULL;
char *seat = NULL;

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

- section = weston_config_get_section(wc, "output", "name", output->name);
weston_config_section_get_string(section, "mode", &s, "preferred");

if (strcmp(s, "off") == 0) {
- weston_output_disable(output);
- free(s);
- return 1;
+ assert(0 && "off was supposed to be pruned");
+ 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) {
@@ -1266,6 +1290,433 @@ drm_backend_output_configure(struct weston_output *output)
return 0;
}

+/* Find the output section to use for configuring the output with the
+ * named head. If an output section with the given name contains
+ * a "same-as" key, ignore all other settings in the output section and
+ * instead find an output section named by the "same-as". Do this
+ * recursively.
+ */
+static struct weston_config_section *
+drm_config_find_controlling_output_section(struct weston_config *config,
+ const char *head_name)
+{
+ struct weston_config_section *section;
+ char *same_as;
+ int depth = 0;
+
+ same_as = strdup(head_name);
+ do {
+ section = weston_config_get_section(config, "output",
+ "name", same_as);
+ if (!section && depth > 0)
+ weston_log("Configuration error: "
+ "output section referred to with "
+ "'same-as=%s' not found.\n", same_as);
+
+ free(same_as);
+
+ if (!section)
+ return NULL;
+
+ if (++depth > 10) {
+ weston_log("Configuration error: "
+ "'same-as' nested too deep for output '%s'.\n",
+ head_name);
+ return NULL;
+ }
+
+ weston_config_section_get_string(section, "same-as",
+ &same_as, NULL);
+ } while (same_as);
+
+ return section;
+}
+
+static struct wet_layoutput *
+wet_compositor_create_layoutput(struct wet_compositor *compositor,
+ const char *name,
+ struct weston_config_section *section)
+{
+ struct wet_layoutput *lo;
+
+ lo = zalloc(sizeof *lo);
+ if (!lo)
+ return NULL;
+
+ lo->compositor = compositor;
+ wl_list_insert(compositor->layoutput_list.prev, &lo->compositor_link);
+ wl_list_init(&lo->output_list);
+ lo->name = strdup(name);
+ lo->section = section;
+
+ return lo;
+}
+
+static void
+wet_layoutput_destroy(struct wet_layoutput *lo)
+{
+ wl_list_remove(&lo->compositor_link);
+ assert(wl_list_empty(&lo->output_list));
+ free(lo->name);
+ free(lo);
+}
+
+static void
+wet_output_handle_destroy(struct wl_listener *listener, void *data)
+{
+ struct wet_output *output;
+
+ output = wl_container_of(listener, output, output_destroy_listener);
+ assert(output->output == data);
+
+ output->output = NULL;
+ wl_list_remove(&output->output_destroy_listener.link);
+}
+
+static struct wet_output *
+wet_layoutput_create_output(struct wet_layoutput *lo, const char *name)
+{
+ struct wet_output *output;
+
+ output = zalloc(sizeof *output);
+ if (!output)
+ return NULL;
+
+ output->output =
+ weston_compositor_create_output(lo->compositor->compositor,
+ name);
+ if (!output) {
+ free(output);
+ return NULL;
+ }
+
+ output->layoutput = lo;
+ wl_list_insert(lo->output_list.prev, &output->link);
+ output->output_destroy_listener.notify = wet_output_handle_destroy;
+ weston_output_add_destroy_listener(output->output,
+ &output->output_destroy_listener);
+
+ return output;
+}
+
+static struct wet_output *
+wet_output_from_weston_output(struct weston_output *base)
+{
+ struct wl_listener *lis;
+
+ lis = weston_output_get_destroy_listener(base,
+ wet_output_handle_destroy);
+ if (!lis)
+ return NULL;
+
+ return container_of(lis, struct wet_output, output_destroy_listener);
+}
+
+static void
+wet_output_destroy(struct wet_output *output)
+{
+ if (output->output)
+ weston_output_destroy(output->output);
+
+ wl_list_remove(&output->link);
+ free(output);
+}
+
+static struct wet_layoutput *
+wet_compositor_find_layoutput(struct wet_compositor *wet, const char *name)
+{
+ struct wet_layoutput *lo;
+
+ wl_list_for_each(lo, &wet->layoutput_list, compositor_link)
+ if (strcmp(lo->name, name) == 0)
+ return lo;
+
+ return NULL;
+}
+
+static void
+wet_compositor_layoutput_add_head(struct wet_compositor *wet,
+ const char *output_name,
+ struct weston_config_section *section,
+ struct weston_head *head)
+{
+ struct wet_layoutput *lo;
+
+ lo = wet_compositor_find_layoutput(wet, output_name);
+ if (!lo) {
+ lo = wet_compositor_create_layoutput(wet, output_name, section);
+ if (!lo)
+ return;
+ }
+
+ if (lo->add.n + 1 >= ARRAY_LENGTH(lo->add.heads))
+ return;
+
+ lo->add.heads[lo->add.n++] = head;
+}
+
+static void
+wet_compositor_destroy_layout(struct wet_compositor *wet)
+{
+ struct wet_layoutput *lo, *lo_tmp;
+ struct wet_output *output, *output_tmp;
+
+ wl_list_for_each_safe(lo, lo_tmp,
+ &wet->layoutput_list, compositor_link) {
+ wl_list_for_each_safe(output, output_tmp,
+ &lo->output_list, link) {
+ wet_output_destroy(output);
+ }
+ wet_layoutput_destroy(lo);
+ }
+}
+
+static void
+drm_head_prepare_enable(struct wet_compositor *wet,
+ struct weston_head *head)
+{
+ const char *name = weston_head_get_name(head);
+ struct weston_config_section *section;
+ char *output_name = NULL;
+ char *mode = NULL;
+
+ section = drm_config_find_controlling_output_section(wet->config, name);
+ if (section) {
+ /* skip outputs that are explicitly off, the backend turns
+ * them off automatically.
+ */
+ weston_config_section_get_string(section, "mode", &mode, NULL);
+ if (mode && strcmp(mode, "off") == 0) {
+ free(mode);
+ return;
+ }
+ free(mode);
+
+ weston_config_section_get_string(section, "name",
+ &output_name, NULL);
+ assert(output_name);
+
+ wet_compositor_layoutput_add_head(wet, output_name,
+ section, head);
+ free(output_name);
+ } else {
+ wet_compositor_layoutput_add_head(wet, name, NULL, head);
+ }
+}
+
+static void
+drm_try_attach(struct weston_output *output,
+ struct wet_head_array *add,
+ struct wet_head_array *failed)
+{
+ unsigned i;
+
+ /* try to attach all heads, this probably succeeds */
+ for (i = 0; i < add->n; i++) {
+ if (!add->heads[i])
+ continue;
+
+ if (weston_output_attach_head(output, add->heads[i]) < 0) {
+ assert(failed->n < ARRAY_LENGTH(failed->heads));
+
+ failed->heads[failed->n++] = add->heads[i];
+ add->heads[i] = NULL;
+ }
+ }
+}
+
+static int
+drm_try_enable(struct weston_output *output,
+ struct wet_head_array *undo,
+ struct wet_head_array *failed)
+{
+ /* Try to enable, and detach heads one by one until it succeeds. */
+ while (!output->enabled) {
+ if (weston_output_enable(output) == 0)
+ return 0;
+
+ /* the next head to drop */
+ while (undo->n > 0 && undo->heads[--undo->n] == NULL)
+ ;
+
+ /* No heads left to undo and failed to enable. */
+ if (undo->heads[undo->n] == NULL)
+ return -1;
+
+ assert(failed->n < ARRAY_LENGTH(failed->heads));
+
+ /* undo one head */
+ weston_head_detach(undo->heads[undo->n]);
+ failed->heads[failed->n++] = undo->heads[undo->n];
+ undo->heads[undo->n] = NULL;
+ }
+
+ return 0;
+}
+
+static int
+drm_try_attach_enable(struct weston_output *output, struct wet_layoutput *lo)
+{
+ struct wet_head_array failed = {};
+ unsigned i;
+
+ assert(!output->enabled);
+
+ drm_try_attach(output, &lo->add, &failed);
+ if (drm_backend_output_configure(output, lo->section) < 0)
+ return -1;
+
+ if (drm_try_enable(output, &lo->add, &failed) < 0)
+ return -1;
+
+ /* For all successfully attached/enabled heads */
+ for (i = 0; i < lo->add.n; i++)
+ if (lo->add.heads[i])
+ wet_head_tracker_create(lo->compositor,
+ lo->add.heads[i]);
+
+ /* Push failed heads to the next round. */
+ lo->add = failed;
+
+ return 0;
+}
+
+static int
+drm_process_layoutput(struct wet_compositor *wet, struct wet_layoutput *lo)
+{
+ struct wet_output *output, *tmp;
+ char *name = NULL;
+ int ret;
+
+ /*
+ * For each existing wet_output:
+ * try attach
+ * While heads left to enable:
+ * Create output
+ * try attach, try enable
+ */
+
+ wl_list_for_each_safe(output, tmp, &lo->output_list, link) {
+ struct wet_head_array failed = {};
+
+ if (!output->output) {
+ /* Clean up left-overs from destroyed heads. */
+ wet_output_destroy(output);
+ continue;
+ }
+
+ assert(output->output->enabled);
+
+ drm_try_attach(output->output, &lo->add, &failed);
+ lo->add = failed;
+ if (lo->add.n == 0)
+ return 0;
+ }
+
+ if (!weston_compositor_find_output_by_name(wet->compositor, lo->name))
+ name = strdup(lo->name);
+
+ while (lo->add.n > 0) {
+ if (!wl_list_empty(&lo->output_list)) {
+ weston_log("Error: independent-CRTC clone mode is not implemented.\n");
+ return -1;
+ }
+
+ if (!name) {
+ ret = asprintf(&name, "%s:%s", lo->name,
+ weston_head_get_name(lo->add.heads[0]));
+ if (ret < 0)
+ return -1;
+ }
+ output = wet_layoutput_create_output(lo, name);
+ free(name);
+ name = NULL;
+
+ if (!output)
+ return -1;
+
+ if (drm_try_attach_enable(output->output, lo) < 0) {
+ wet_output_destroy(output);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+drm_process_layoutputs(struct wet_compositor *wet)
+{
+ struct wet_layoutput *lo;
+ int ret = 0;
+
+ wl_list_for_each(lo, &wet->layoutput_list, compositor_link) {
+ if (lo->add.n == 0)
+ continue;
+
+ if (drm_process_layoutput(wet, lo) < 0) {
+ lo->add = (struct wet_head_array){};
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static void
+drm_head_disable(struct weston_head *head)
+{
+ struct weston_output *output_base;
+ struct wet_output *output;
+ struct wet_head_tracker *track;
+
+ track = wet_head_tracker_from_head(head);
+ if (track)
+ wet_head_tracker_destroy(track);
+
+ output_base = weston_head_get_output(head);
+ assert(output_base);
+ output = wet_output_from_weston_output(output_base);
+ assert(output && output->output == output_base);
+
+ weston_head_detach(head);
+ if (count_remaining_heads(output->output, NULL) == 0)
+ wet_output_destroy(output);
+}
+
+static void
+drm_heads_changed(struct weston_compositor *compositor)
+{
+ struct wet_compositor *wet = to_wet_compositor(compositor);
+ struct weston_head *head = NULL;
+ bool connected;
+ bool enabled;
+ bool changed;
+
+ /* We need to collect all cloned heads into outputs before enabling the
+ * output.
+ */
+ 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) {
+ drm_head_prepare_enable(wet, head);
+ } else if (!connected && enabled) {
+ drm_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);
+ }
+
+ if (drm_process_layoutputs(wet) < 0)
+ wet->init_failed = true;
+}
+
static int
load_drm_backend(struct weston_compositor *c,
int *argc, char **argv, struct weston_config *wc)
@@ -1298,7 +1749,7 @@ 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);
+ weston_compositor_set_heads_changed_cb(c, drm_heads_changed);

ret = weston_compositor_load_backend(c, WESTON_BACKEND_DRM,
&config.base);
@@ -1845,6 +2296,8 @@ int main(int argc, char *argv[])
{ WESTON_OPTION_BOOLEAN, "wait-for-debugger", 0, &wait_for_debugger },
};

+ wl_list_init(&wet.layoutput_list);
+
if (os_fd_set_cloexec(fileno(stdin))) {
printf("Unable to set stdin as close on exec().\n");
return EXIT_FAILURE;
@@ -2032,6 +2485,8 @@ int main(int argc, char *argv[])
ret = wet.compositor->exit_code;

out:
+ wet_compositor_destroy_layout(&wet);
+
/* free(NULL) is valid, and it won't be NULL if it's used */
free(wet.parsed_options);

diff --git a/man/weston-drm.man b/man/weston-drm.man
index 75d79021..257237df 100644
--- a/man/weston-drm.man
+++ b/man/weston-drm.man
@@ -79,6 +79,18 @@ Transform for the output, which can be rotated in 90-degree steps
and possibly flipped. Possible values are
.BR normal ", " 90 ", " 180 ", " 270 ", "
.BR flipped ", " flipped-90 ", " flipped-180 ", and " flipped-270 .
+.TP
+\fBsame-as\fR=\fIname\fR
+Make this output (connector) a clone of another. The argument
+.IR name " is the "
+.BR name " value of another output section. The
+referred to output section must exist. When this key is present in an
+output section, all other keys have no effect on the configuration.
+
+NOTE: cms-colord plugin does not work correctly with this option. The plugin
+chooses an arbitrary monitor to load the color profile for, but the
+profile is applied equally to all cloned monitors regardless of their
+properties.
.
.\" ***************************************************************
.SH OPTIONS
--
2.13.6
Ray, Ian (GE Healthcare)
2018-03-23 13:12:27 UTC
Permalink
Post by Pekka Paalanen
Hi all,
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
https://lists.freedesktop.org/archives/wayland-devel/2017-December/036236.html
https://phabricator.freedesktop.org/T7727
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://gitlab.collabora.com/pq/weston/commits/clonemode-6
- DRM-backend migration
- shared-CRTC clone mode implemented in DRM backend
- applied review comments from v5
- don't create a weston_output just to turn it off (DRM)
- cms-colord will print a warning when used with clone mode
- cms-colord vs. clone mode mentioned in weston-drm.man
Unfortunately the testing results are not 100%, you can find my testing
procedure below the diffstat.
- Patches 1-17 introduce most of the new head-based API, and build the
scaffolding that allows migrating the frontends and the backends one
by one.
- Patches 18-24 migrate the frontends one by one. Patch 19 introduces
the simple output configurator which is first used for all backends,
including the DRM-backend.
- Patches 25-30 clean up libweston after the frontend migration.
- Patches 31-38 migrate all the backends to the head-based API. At this
point the DRM-backend migration is basically a fake, though.
- Patch 39 removes weston_output::head with the last bits of the
scaffolding.
- Patches 40-44 enhances libweston core to better support the
DRM-backend's clone mode configuration and improve logging.
- Patches 45-55 implement the head-based API for real in the
DRM-backend, culminating in patch 55 which creates heads for all
connectors.
- Patch 56 removes unused_connectors array which has been replace with
the head list.
- Patches 57-70 finally implement everything needed for shared-CRTC
clone mode in the DRM-backend.
- Patches 71-73 add a new output configrator logic in the frontend to
handle clone mode, supporting a new weston.ini key "same-as".
Do you think we should call the weston.ini key "clone-of" instead, to
leave "same-as" reserved for clone mode where only the desktop area is
the same but the monitors would have different video modes, scaling,
etc.?
- get me shared-CRTC clone mode or fail
- get me clone mode, preferring shared-CRTC but automatic fallback to
independent CRTCs
- get me clone mode, but I want one monitor with HiDPI and one with
LoDPI
Here I have implemented the automatic fallback version, except it cannot
actually fall back, because we have no API to make two weston_outputs
show the same area, and libweston's damage tracking could not handle it
even if we could.
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 weston_head_is_enabled()
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-drm: start migration to head-based output API
libweston: remove weston_output::head
libweston: print head names on output enable
libweston: create/find output by name
libweston: support user data on weston_output
libweston: allow attaching heads to enabled outputs
libweston: log head detach on enabled output
compositor-drm: drm_output_find_by_connector from head_list
compositor-drm: use head_find_by_connector in update_unused_outputs
compositor-drm: find disconnects from head_list
compositor-drm: move backlight into drm_head
compositor-drm: move connector fields into drm_head
compositor-drm: allocate CRTC on enable()
compositor-drm: simplify drm_output_find_by_crtc()
compositor-drm: simplify drm_output_find_special_plane()
compositor-drm: get current mode on head discovery
compositor-drm: move mode list to set_mode()
compositor-drm: create heads for all connectors
compositor-drm: remove unused_connectors array
compositor-drm: drm_output_apply_state_legacy heads
compositor-drm: drm_output_apply_state_atomic heads
compositor-drm: drm_set_backlight heads
compositor-drm: unify head status logging
compositor-drm: combine mode list from heads
compositor-drm: backlight control for all heads
compositor-drm: update video mode printing
compositor-drm: introduce drm_head_read_current_setup()
compositor-drm: no need to clear inherited_mode
compositor-drm: rewrite crtc picking for clone mode
compositor-drm: preserve CRTC routing harder
compositor-drm: head detach requires a modeset
compositor-drm: head attach requires a modeset
compositor-drm: allow shared-CRTC cloning
weston: store weston_compositor in wet_compositor
weston: use wet.compositor consistently in main()
weston: support clone mode on DRM-frontend
compositor/cms-colord.c | 45 +-
compositor/main.c | 818 +++++++++++++++++++++----
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-v6.c | 2 +-
libweston/compositor-drm.c | 1128 +++++++++++++++++++++++------------
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 | 1107 +++++++++++++++++++++++++++++++---
libweston/compositor.h | 198 +++++-
libweston/windowed-output-api.h | 23 +-
man/weston-drm.man | 12 +
tests/weston-test.c | 2 +-
20 files changed, 3249 insertions(+), 776 deletions(-)
All tests were successful except where otherwise mentioned.
$ make -j7 distcheck
$ weston
$ weston --output-count=3
- close x11 windows one by one
$ weston --fullscreen (equally broken before and after)
parent: $ weston --output-count=2 --width=700
outputs have different height
weston --use-pixman
weston --use-pixman --fullscreen
weston --use-pixman --output-count=2
weston --use-pixman --sprawl
- close parent outputs one by one
In the --sprawl case, closing the first output soon results in the
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
467 wl_list_for_each_safe(l, next, &signal->listener_list, link)
(gdb) bt
#0 0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
#1 0x00007ffff7ba8a4c in weston_surface_destroy (surface=0x555555da74a0) at /home/pq/git/weston/libweston/compositor.c:1904
#2 0x00007fffee108196 in fade_out_done_idle_cb (data=0x555555da7d20) at /home/pq/git/weston/desktop-shell/shell.c:2281
#5 0x00007ffff798c4fa in wl_display_run (display=0x55555576c170) at src/wayland-server.c:1260
#6 0x000055555555dd86 in main (argc=1, argv=0x7fffffffdb58) at /home/pq/git/weston/compositor/main.c:2477
parent: $ weston --output-count=2 --width=700 --shell=fullscreen-shell.so
weston --use-pixman
- close parent outputs one by one
- using remmina as client, connected twice in a row
$ weston -Bfbdev-backend.so
Two outputs plugged in, third unplugged.
1. start Weston, ensure both outputs work
2. unplug one output, ensure remaining output works
3. re-plug the output, ensure both outputs work
4. quit
One output plugged in, the rest unplugged. The plugged-in output is
configured to be off in weston.ini.
1. start Weston, zero outputs, check via remote login weston-info works
2. plug in an output, check it works
3. unplug the output, check weston-info via remote
Thanks,
pq
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Ray, Ian (GE Healthcare)
2018-03-23 17:16:30 UTC
Permalink
Post by Pekka Paalanen
Hi all,
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
https://lists.freedesktop.org/archives/wayland-devel/2017-December/036236.html
https://phabricator.freedesktop.org/T7727
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://gitlab.collabora.com/pq/weston/commits/clonemode-6
- DRM-backend migration
- shared-CRTC clone mode implemented in DRM backend
- applied review comments from v5
- don't create a weston_output just to turn it off (DRM)
- cms-colord will print a warning when used with clone mode
- cms-colord vs. clone mode mentioned in weston-drm.man
Unfortunately the testing results are not 100%, you can find my testing
procedure below the diffstat.
- Patches 1-17 introduce most of the new head-based API, and build the
scaffolding that allows migrating the frontends and the backends one
by one.
- Patches 18-24 migrate the frontends one by one. Patch 19 introduces
the simple output configurator which is first used for all backends,
including the DRM-backend.
- Patches 25-30 clean up libweston after the frontend migration.
- Patches 31-38 migrate all the backends to the head-based API. At this
point the DRM-backend migration is basically a fake, though.
- Patch 39 removes weston_output::head with the last bits of the
scaffolding.
- Patches 40-44 enhances libweston core to better support the
DRM-backend's clone mode configuration and improve logging.
- Patches 45-55 implement the head-based API for real in the
DRM-backend, culminating in patch 55 which creates heads for all
connectors.
- Patch 56 removes unused_connectors array which has been replace with
the head list.
- Patches 57-70 finally implement everything needed for shared-CRTC
clone mode in the DRM-backend.
- Patches 71-73 add a new output configrator logic in the frontend to
handle clone mode, supporting a new weston.ini key "same-as".
Do you think we should call the weston.ini key "clone-of" instead, to
leave "same-as" reserved for clone mode where only the desktop area is
the same but the monitors would have different video modes, scaling,
etc.?
- get me shared-CRTC clone mode or fail
- get me clone mode, preferring shared-CRTC but automatic fallback to
independent CRTCs
- get me clone mode, but I want one monitor with HiDPI and one with
LoDPI
Here I have implemented the automatic fallback version, except it cannot
actually fall back, because we have no API to make two weston_outputs
show the same area, and libweston's damage tracking could not handle it
even if we could.
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 weston_head_is_enabled()
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-drm: start migration to head-based output API
libweston: remove weston_output::head
libweston: print head names on output enable
libweston: create/find output by name
libweston: support user data on weston_output
libweston: allow attaching heads to enabled outputs
libweston: log head detach on enabled output
compositor-drm: drm_output_find_by_connector from head_list
compositor-drm: use head_find_by_connector in update_unused_outputs
compositor-drm: find disconnects from head_list
compositor-drm: move backlight into drm_head
compositor-drm: move connector fields into drm_head
compositor-drm: allocate CRTC on enable()
compositor-drm: simplify drm_output_find_by_crtc()
compositor-drm: simplify drm_output_find_special_plane()
compositor-drm: get current mode on head discovery
compositor-drm: move mode list to set_mode()
compositor-drm: create heads for all connectors
compositor-drm: remove unused_connectors array
compositor-drm: drm_output_apply_state_legacy heads
compositor-drm: drm_output_apply_state_atomic heads
compositor-drm: drm_set_backlight heads
compositor-drm: unify head status logging
compositor-drm: combine mode list from heads
compositor-drm: backlight control for all heads
compositor-drm: update video mode printing
compositor-drm: introduce drm_head_read_current_setup()
compositor-drm: no need to clear inherited_mode
compositor-drm: rewrite crtc picking for clone mode
compositor-drm: preserve CRTC routing harder
compositor-drm: head detach requires a modeset
compositor-drm: head attach requires a modeset
compositor-drm: allow shared-CRTC cloning
weston: store weston_compositor in wet_compositor
weston: use wet.compositor consistently in main()
weston: support clone mode on DRM-frontend
compositor/cms-colord.c | 45 +-
compositor/main.c | 818 +++++++++++++++++++++----
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-v6.c | 2 +-
libweston/compositor-drm.c | 1128 +++++++++++++++++++++++------------
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 | 1107 +++++++++++++++++++++++++++++++---
libweston/compositor.h | 198 +++++-
libweston/windowed-output-api.h | 23 +-
man/weston-drm.man | 12 +
tests/weston-test.c | 2 +-
20 files changed, 3249 insertions(+), 776 deletions(-)
All tests were successful except where otherwise mentioned.
$ make -j7 distcheck
$ weston
$ weston --output-count=3
- close x11 windows one by one
$ weston --fullscreen (equally broken before and after)
parent: $ weston --output-count=2 --width=700
outputs have different height
weston --use-pixman
weston --use-pixman --fullscreen
weston --use-pixman --output-count=2
weston --use-pixman --sprawl
- close parent outputs one by one
In the --sprawl case, closing the first output soon results in the
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
467 wl_list_for_each_safe(l, next, &signal->listener_list, link)
(gdb) bt
#0 0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
#1 0x00007ffff7ba8a4c in weston_surface_destroy (surface=0x555555da74a0) at /home/pq/git/weston/libweston/compositor.c:1904
#2 0x00007fffee108196 in fade_out_done_idle_cb (data=0x555555da7d20) at /home/pq/git/weston/desktop-shell/shell.c:2281
#5 0x00007ffff798c4fa in wl_display_run (display=0x55555576c170) at src/wayland-server.c:1260
#6 0x000055555555dd86 in main (argc=1, argv=0x7fffffffdb58) at /home/pq/git/weston/compositor/main.c:2477
parent: $ weston --output-count=2 --width=700 --shell=fullscreen-shell.so
weston --use-pixman
- close parent outputs one by one
- using remmina as client, connected twice in a row
$ weston -Bfbdev-backend.so
Two outputs plugged in, third unplugged.
1. start Weston, ensure both outputs work
2. unplug one output, ensure remaining output works
3. re-plug the output, ensure both outputs work
4. quit
One output plugged in, the rest unplugged. The plugged-in output is
configured to be off in weston.ini.
1. start Weston, zero outputs, check via remote login weston-info works
2. plug in an output, check it works
3. unplug the output, check weston-info via remote
Thanks,
pq
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Ray, Ian (GE Healthcare)
2018-03-24 10:16:12 UTC
Permalink
Post by Pekka Paalanen
Post by Pekka Paalanen
Hi all,
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
https://lists.freedesktop.org/archives/wayland-devel/2017-December/036236.html
https://phabricator.freedesktop.org/T7727
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://gitlab.collabora.com/pq/weston/commits/clonemode-6
- DRM-backend migration
- shared-CRTC clone mode implemented in DRM backend
- applied review comments from v5
- don't create a weston_output just to turn it off (DRM)
- cms-colord will print a warning when used with clone mode
- cms-colord vs. clone mode mentioned in weston-drm.man
Unfortunately the testing results are not 100%, you can find my testing
procedure below the diffstat.
- Patches 1-17 introduce most of the new head-based API, and build the
scaffolding that allows migrating the frontends and the backends one
by one.
- Patches 18-24 migrate the frontends one by one. Patch 19 introduces
the simple output configurator which is first used for all backends,
including the DRM-backend.
- Patches 25-30 clean up libweston after the frontend migration.
- Patches 31-38 migrate all the backends to the head-based API. At this
point the DRM-backend migration is basically a fake, though.
- Patch 39 removes weston_output::head with the last bits of the
scaffolding.
Patches 31-39 : Reviewed-by Ian Ray <***@ge.com>

(May I say that I am really enjoying the review so far. The commits
are well structured and the commit messages are highly informative.)
Post by Pekka Paalanen
Post by Pekka Paalanen
- Patches 40-44 enhances libweston core to better support the
DRM-backend's clone mode configuration and improve logging.
- Patches 45-55 implement the head-based API for real in the
DRM-backend, culminating in patch 55 which creates heads for all
connectors.
- Patch 56 removes unused_connectors array which has been replace with
the head list.
- Patches 57-70 finally implement everything needed for shared-CRTC
clone mode in the DRM-backend.
- Patches 71-73 add a new output configrator logic in the frontend to
handle clone mode, supporting a new weston.ini key "same-as".
Do you think we should call the weston.ini key "clone-of" instead, to
leave "same-as" reserved for clone mode where only the desktop area is
the same but the monitors would have different video modes, scaling,
etc.?
- get me shared-CRTC clone mode or fail
- get me clone mode, preferring shared-CRTC but automatic fallback to
independent CRTCs
- get me clone mode, but I want one monitor with HiDPI and one with
LoDPI
Here I have implemented the automatic fallback version, except it cannot
actually fall back, because we have no API to make two weston_outputs
show the same area, and libweston's damage tracking could not handle it
even if we could.
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 weston_head_is_enabled()
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-drm: start migration to head-based output API
libweston: remove weston_output::head
libweston: print head names on output enable
libweston: create/find output by name
libweston: support user data on weston_output
libweston: allow attaching heads to enabled outputs
libweston: log head detach on enabled output
compositor-drm: drm_output_find_by_connector from head_list
compositor-drm: use head_find_by_connector in update_unused_outputs
compositor-drm: find disconnects from head_list
compositor-drm: move backlight into drm_head
compositor-drm: move connector fields into drm_head
compositor-drm: allocate CRTC on enable()
compositor-drm: simplify drm_output_find_by_crtc()
compositor-drm: simplify drm_output_find_special_plane()
compositor-drm: get current mode on head discovery
compositor-drm: move mode list to set_mode()
compositor-drm: create heads for all connectors
compositor-drm: remove unused_connectors array
compositor-drm: drm_output_apply_state_legacy heads
compositor-drm: drm_output_apply_state_atomic heads
compositor-drm: drm_set_backlight heads
compositor-drm: unify head status logging
compositor-drm: combine mode list from heads
compositor-drm: backlight control for all heads
compositor-drm: update video mode printing
compositor-drm: introduce drm_head_read_current_setup()
compositor-drm: no need to clear inherited_mode
compositor-drm: rewrite crtc picking for clone mode
compositor-drm: preserve CRTC routing harder
compositor-drm: head detach requires a modeset
compositor-drm: head attach requires a modeset
compositor-drm: allow shared-CRTC cloning
weston: store weston_compositor in wet_compositor
weston: use wet.compositor consistently in main()
weston: support clone mode on DRM-frontend
compositor/cms-colord.c | 45 +-
compositor/main.c | 818 +++++++++++++++++++++----
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-v6.c | 2 +-
libweston/compositor-drm.c | 1128 +++++++++++++++++++++++------------
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 | 1107 +++++++++++++++++++++++++++++++---
libweston/compositor.h | 198 +++++-
libweston/windowed-output-api.h | 23 +-
man/weston-drm.man | 12 +
tests/weston-test.c | 2 +-
20 files changed, 3249 insertions(+), 776 deletions(-)
All tests were successful except where otherwise mentioned.
$ make -j7 distcheck
$ weston
$ weston --output-count=3
- close x11 windows one by one
$ weston --fullscreen (equally broken before and after)
parent: $ weston --output-count=2 --width=700
outputs have different height
weston --use-pixman
weston --use-pixman --fullscreen
weston --use-pixman --output-count=2
weston --use-pixman --sprawl
- close parent outputs one by one
In the --sprawl case, closing the first output soon results in the
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
467 wl_list_for_each_safe(l, next, &signal->listener_list, link)
(gdb) bt
#0 0x00007ffff7ba3b8f in wl_signal_emit (signal=0x555555da74a8, data=0x555555da74a0) at /home/pq/local/include/wayland-server-core.h:467
#1 0x00007ffff7ba8a4c in weston_surface_destroy (surface=0x555555da74a0) at /home/pq/git/weston/libweston/compositor.c:1904
#2 0x00007fffee108196 in fade_out_done_idle_cb (data=0x555555da7d20) at /home/pq/git/weston/desktop-shell/shell.c:2281
#5 0x00007ffff798c4fa in wl_display_run (display=0x55555576c170) at src/wayland-server.c:1260
#6 0x000055555555dd86 in main (argc=1, argv=0x7fffffffdb58) at /home/pq/git/weston/compositor/main.c:2477
parent: $ weston --output-count=2 --width=700 --shell=fullscreen-shell.so
weston --use-pixman
- close parent outputs one by one
- using remmina as client, connected twice in a row
$ weston -Bfbdev-backend.so
Two outputs plugged in, third unplugged.
1. start Weston, ensure both outputs work
2. unplug one output, ensure remaining output works
3. re-plug the output, ensure both outputs work
4. quit
One output plugged in, the rest unplugged. The plugged-in output is
configured to be off in weston.ini.
1. start Weston, zero outputs, check via remote login weston-info works
2. plug in an output, check it works
3. unplug the output, check weston-info via remote
Thanks,
pq
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2018-04-10 13:12:20 UTC
Permalink
On Sat, 24 Mar 2018 10:16:12 +0000
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
Hi all,
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
https://lists.freedesktop.org/archives/wayland-devel/2017-December/036236.html
https://phabricator.freedesktop.org/T7727
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://gitlab.collabora.com/pq/weston/commits/clonemode-6
- DRM-backend migration
- shared-CRTC clone mode implemented in DRM backend
- applied review comments from v5
- don't create a weston_output just to turn it off (DRM)
- cms-colord will print a warning when used with clone mode
- cms-colord vs. clone mode mentioned in weston-drm.man
Unfortunately the testing results are not 100%, you can find my testing
procedure below the diffstat.
- Patches 1-17 introduce most of the new head-based API, and build the
scaffolding that allows migrating the frontends and the backends one
by one.
- Patches 18-24 migrate the frontends one by one. Patch 19 introduces
the simple output configurator which is first used for all backends,
including the DRM-backend.
- Patches 25-30 clean up libweston after the frontend migration.
- Patches 31-38 migrate all the backends to the head-based API. At this
point the DRM-backend migration is basically a fake, though.
- Patch 39 removes weston_output::head with the last bits of the
scaffolding.
(May I say that I am really enjoying the review so far. The commits
are well structured and the commit messages are highly informative.)
Thank you very much for the reviews, Ian!

I have addressed your nitpicks and added your R-bs to the first 39
patches you went through. These can be found in the branch:

https://gitlab.collabora.com/pq/weston/commits/clonemode-7-part1

I should probably test that a bit more before pushing.

Now I'd just need an Acked-by or two from our oldtimers to land that
batch. After that there would be 34 patches left to review in this
series.


Thanks,
pq
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
- Patches 40-44 enhances libweston core to better support the
DRM-backend's clone mode configuration and improve logging.
- Patches 45-55 implement the head-based API for real in the
DRM-backend, culminating in patch 55 which creates heads for all
connectors.
- Patch 56 removes unused_connectors array which has been replace with
the head list.
- Patches 57-70 finally implement everything needed for shared-CRTC
clone mode in the DRM-backend.
- Patches 71-73 add a new output configrator logic in the frontend to
handle clone mode, supporting a new weston.ini key "same-as".
Do you think we should call the weston.ini key "clone-of" instead, to
leave "same-as" reserved for clone mode where only the desktop area is
the same but the monitors would have different video modes, scaling,
etc.?
- get me shared-CRTC clone mode or fail
- get me clone mode, preferring shared-CRTC but automatic fallback to
independent CRTCs
- get me clone mode, but I want one monitor with HiDPI and one with
LoDPI
Derek Foreman
2018-04-10 13:56:41 UTC
Permalink
Post by Pekka Paalanen
On Sat, 24 Mar 2018 10:16:12 +0000
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
Hi all,
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
https://lists.freedesktop.org/archives/wayland-devel/2017-December/036236.html
https://phabricator.freedesktop.org/T7727
https://phabricator.freedesktop.org/w/wayland/weston/atomic-output-config/
https://lists.freedesktop.org/archives/wayland-devel/2017-October/035604.html
https://gitlab.collabora.com/pq/weston/commits/clonemode-6
- DRM-backend migration
- shared-CRTC clone mode implemented in DRM backend
- applied review comments from v5
- don't create a weston_output just to turn it off (DRM)
- cms-colord will print a warning when used with clone mode
- cms-colord vs. clone mode mentioned in weston-drm.man
Unfortunately the testing results are not 100%, you can find my testing
procedure below the diffstat.
- Patches 1-17 introduce most of the new head-based API, and build the
scaffolding that allows migrating the frontends and the backends one
by one.
- Patches 18-24 migrate the frontends one by one. Patch 19 introduces
the simple output configurator which is first used for all backends,
including the DRM-backend.
- Patches 25-30 clean up libweston after the frontend migration.
- Patches 31-38 migrate all the backends to the head-based API. At this
point the DRM-backend migration is basically a fake, though.
- Patch 39 removes weston_output::head with the last bits of the
scaffolding.
(May I say that I am really enjoying the review so far. The commits
are well structured and the commit messages are highly informative.)
Thank you very much for the reviews, Ian!
I have addressed your nitpicks and added your R-bs to the first 39
https://gitlab.collabora.com/pq/weston/commits/clonemode-7-part1
I should probably test that a bit more before pushing.
Now I'd just need an Acked-by or two from our oldtimers to land that
batch. After that there would be 34 patches left to review in this
series.
Well, the whole series can certainly be
Acked-by: Derek Foreman <***@osg.samsung.com>

though I might not find time to review it in the near future.

Thanks,
Derek
Post by Pekka Paalanen
Thanks,
pq
Post by Ray, Ian (GE Healthcare)
Post by Pekka Paalanen
- Patches 40-44 enhances libweston core to better support the
DRM-backend's clone mode configuration and improve logging.
- Patches 45-55 implement the head-based API for real in the
DRM-backend, culminating in patch 55 which creates heads for all
connectors.
- Patch 56 removes unused_connectors array which has been replace with
the head list.
- Patches 57-70 finally implement everything needed for shared-CRTC
clone mode in the DRM-backend.
- Patches 71-73 add a new output configrator logic in the frontend to
handle clone mode, supporting a new weston.ini key "same-as".
Do you think we should call the weston.ini key "clone-of" instead, to
leave "same-as" reserved for clone mode where only the desktop area is
the same but the monitors would have different video modes, scaling,
etc.?
- get me shared-CRTC clone mode or fail
- get me clone mode, preferring shared-CRTC but automatic fallback to
independent CRTCs
- get me clone mode, but I want one monitor with HiDPI and one with
LoDPI
Daniel Stone
2018-04-12 10:09:26 UTC
Permalink
Hi Pekka,
Post by Pekka Paalanen
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
I reviewed clonemode-7-part1 from
https://gitlab.collabora.com/pq/weston, which is slightly revised (&
significantly truncated) compared to this series. Here's a braindump:

When adding head_list to a weston_output, we can't send
wp_presentation_feedback's sync_output event to the same resource for
multiple wl_outputs:
https://gitlab.collabora.com/pq/weston/commit/d0f5afff9213b81c329a98cdc7593a0101f953d5#note_7491

Should be a pretty easy fix (don't iterate through multiple heads once
you've sent an event to at least one head's resource), but I don't
know what the long-term fix is. Maybe there isn't a good one.

I think this is also a small and easily-fixable bug:
https://gitlab.collabora.com/pq/weston/commit/55206c5e8453722caed18d7fc6e3d9ed0966e623#note_7496

The only real interface change I'd like to see is using a listener
list rather than a single callback:
https://gitlab.collabora.com/pq/weston/commit/ebb74da3c89d90810f8209340da33faeea3169bd#note_7493

Here are a couple of throwaway comments and nitpicks; feel free to ignore them:
https://gitlab.collabora.com/pq/weston/commit/199fda53546128afb6c0553749dfa85bd00799a4#note_7492
https://gitlab.collabora.com/pq/weston/commit/c618aa3dc9c2046e5540bbe444375f79e2e2c71f#note_7494
https://gitlab.collabora.com/pq/weston/commit/6a20dd4e16ae75431149c19e42b52eaf2e179b8c#note_7495

The rest looks good to me! I think it'd be good to merge 'libweston:
cancel idle_repaint on output destroy' ASAP in particular. The fbdev
and RDP patches are quite lazily Acked-by me, as I lost the will to
live whilst trying to review them. The rest of the series, with at
least the first three addressed, is Reviewed-by me.

I like both sides of the new API a lot. Great stuff!

Cheers,
Daniel
Pekka Paalanen
2018-04-13 14:16:14 UTC
Permalink
On Thu, 12 Apr 2018 12:09:26 +0200
Post by Daniel Stone
Hi Pekka,
Post by Pekka Paalanen
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
I reviewed clonemode-7-part1 from
https://gitlab.collabora.com/pq/weston, which is slightly revised (&
When adding head_list to a weston_output, we can't send
wp_presentation_feedback's sync_output event to the same resource for
https://gitlab.collabora.com/pq/weston/commit/d0f5afff9213b81c329a98cdc7593a0101f953d5#note_7491
Fixed.
Post by Daniel Stone
Should be a pretty easy fix (don't iterate through multiple heads once
you've sent an event to at least one head's resource), but I don't
know what the long-term fix is. Maybe there isn't a good one.
I think the fix there is very much sufficient.
Post by Daniel Stone
https://gitlab.collabora.com/pq/weston/commit/55206c5e8453722caed18d7fc6e3d9ed0966e623#note_7496
Unfortunately, it's complicated and not a bug. :-P
Explanation in Gitlab.
Post by Daniel Stone
The only real interface change I'd like to see is using a listener
https://gitlab.collabora.com/pq/weston/commit/ebb74da3c89d90810f8209340da33faeea3169bd#note_7493
Done.
Post by Daniel Stone
https://gitlab.collabora.com/pq/weston/commit/199fda53546128afb6c0553749dfa85bd00799a4#note_7492
https://gitlab.collabora.com/pq/weston/commit/c618aa3dc9c2046e5540bbe444375f79e2e2c71f#note_7494
https://gitlab.collabora.com/pq/weston/commit/6a20dd4e16ae75431149c19e42b52eaf2e179b8c#note_7495
Yeah.
Post by Daniel Stone
cancel idle_repaint on output destroy' ASAP in particular. The fbdev
and RDP patches are quite lazily Acked-by me, as I lost the will to
live whilst trying to review them. The rest of the series, with at
least the first three addressed, is Reviewed-by me.
Your R-b and A-b should be in place, the revised series is at
https://gitlab.collabora.com/pq/weston/commits/clonemode-8-part1

I ran through my test procedure from the v6 cover-letter, and
everything worked as expected there, except for the DRM-backend tests.

drmModeAtomicCommit() fails with -EBUSY after the "start with two
outputs, unplug and re-plug one" test. It happens already on upstream
master, so it's not introduced by this series. The panel has seconds
showing, and I am unplugging and replugging a DisplayPort monitor.

The zero outputs test fails on hotplug which looks like my bad in this
series, so I need to fix that before we land this. The test works fine
on upstream master.


Thanks,
pq
Pekka Paalanen
2018-04-17 12:47:31 UTC
Permalink
On Fri, 13 Apr 2018 17:16:14 +0300
Post by Pekka Paalanen
On Thu, 12 Apr 2018 12:09:26 +0200
Post by Daniel Stone
Hi Pekka,
Post by Pekka Paalanen
here is the v6 of the shared-CRTC clone mode series. Since v5, quite
many patches have been extracted from this series, sent out and merged
upstream. However, now the series is bigger than ever, because here I am
posting the complete series, including the full DRM-backend migration
and DRM shared-CRTC clone mode implementation, thanks to having the
basic atomic modesetting landed upstream.
I reviewed clonemode-7-part1 from
https://gitlab.collabora.com/pq/weston, which is slightly revised (&
When adding head_list to a weston_output, we can't send
wp_presentation_feedback's sync_output event to the same resource for
https://gitlab.collabora.com/pq/weston/commit/d0f5afff9213b81c329a98cdc7593a0101f953d5#note_7491
Fixed.
Post by Daniel Stone
Should be a pretty easy fix (don't iterate through multiple heads once
you've sent an event to at least one head's resource), but I don't
know what the long-term fix is. Maybe there isn't a good one.
I think the fix there is very much sufficient.
Post by Daniel Stone
https://gitlab.collabora.com/pq/weston/commit/55206c5e8453722caed18d7fc6e3d9ed0966e623#note_7496
Unfortunately, it's complicated and not a bug. :-P
Explanation in Gitlab.
Post by Daniel Stone
The only real interface change I'd like to see is using a listener
https://gitlab.collabora.com/pq/weston/commit/ebb74da3c89d90810f8209340da33faeea3169bd#note_7493
Done.
Post by Daniel Stone
https://gitlab.collabora.com/pq/weston/commit/199fda53546128afb6c0553749dfa85bd00799a4#note_7492
https://gitlab.collabora.com/pq/weston/commit/c618aa3dc9c2046e5540bbe444375f79e2e2c71f#note_7494
https://gitlab.collabora.com/pq/weston/commit/6a20dd4e16ae75431149c19e42b52eaf2e179b8c#note_7495
Yeah.
Post by Daniel Stone
cancel idle_repaint on output destroy' ASAP in particular. The fbdev
and RDP patches are quite lazily Acked-by me, as I lost the will to
live whilst trying to review them. The rest of the series, with at
least the first three addressed, is Reviewed-by me.
Your R-b and A-b should be in place, the revised series is at
https://gitlab.collabora.com/pq/weston/commits/clonemode-8-part1
If one looked close enough at that branch, you'd see the commit
"libweston: new head-based output management API" duplicated. I've
fixed that.
Post by Pekka Paalanen
I ran through my test procedure from the v6 cover-letter, and
everything worked as expected there, except for the DRM-backend tests.
drmModeAtomicCommit() fails with -EBUSY after the "start with two
outputs, unplug and re-plug one" test. It happens already on upstream
master, so it's not introduced by this series. The panel has seconds
showing, and I am unplugging and replugging a DisplayPort monitor.
The zero outputs test fails on hotplug which looks like my bad in this
series, so I need to fix that before we land this. The test works fine
on upstream master.
Found the cause of the zero output hotplug failure, and it is because
of how the intermediate code works: libweston core is completely
migrated but as long as the DRM-backend fakes it, there's a crasher if
you have configured an output to be "off" in weston.ini and hotplugs
happen. The problem will disappear when the frontend stops creating an
output just to be able to turn it off, or the DRM-backend starts
properly creating outputs as the head-based output API expects. Anyway,
I've added a workaround in commit "weston: migrate DRM to head-based
output API", and now the test works.

The first 39 patches of the clone mode series, as present in the branch
https://gitlab.collabora.com/pq/weston/commits/clonemode-9-part1
are now pushed into master:
61c4a730..42c0e148 master -> master


Thanks,
pq

Loading...