[PATCH 1/9] compositor-drm: Add per connector clone mode support

Xiong Zhang xiong.y.zhang at intel.com
Thu Feb 13 23:17:36 PST 2014


Add a clone option in output section of weston.ini

Master output will be in compositor->output_list.
Slave output won't be in compositor->output_list, while it is in
master->clone_output_list.

Slave output's current mode size should be smaller than master
output's mode size, or else the mode setting for slave output
will fail. So slave output's mode will be adjusted when it is
necessary.

Signed-off-by: Xiong Zhang <xiong.y.zhang at intel.com>
---
 src/compositor-drm.c | 189 ++++++++++++++++++++++++++++++++++++++++++++-------
 src/compositor.c     |  15 +++-
 src/compositor.h     |   5 ++
 weston.ini.in        |   2 +
 4 files changed, 185 insertions(+), 26 deletions(-)

diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index e45f47d..6773226 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -87,6 +87,7 @@ struct drm_compositor {
 	uint32_t connector_allocator;
 	struct wl_listener session_listener;
 	uint32_t format;
+	struct wl_list clone_output_list;
 
 	/* we need these parameters in order to not fail drmModeAddFB2()
 	 * due to out of bounds dimensions, and then mistakenly set
@@ -152,6 +153,9 @@ struct drm_output {
 	int page_flip_pending;
 	int destroy_pending;
 
+	uint32_t transform;
+	int scale, mm_width, mm_height;
+
 	struct gbm_surface *surface;
 	struct gbm_bo *cursor_bo[2];
 	struct weston_plane cursor_plane;
@@ -1856,7 +1860,7 @@ static int
 create_output_for_connector(struct drm_compositor *ec,
 			    drmModeRes *resources,
 			    drmModeConnector *connector,
-			    int x, int y, struct udev_device *drm_device)
+			    struct udev_device *drm_device)
 {
 	struct drm_output *output;
 	struct drm_mode *drm_mode, *next, *preferred, *current, *configured, *best;
@@ -1870,6 +1874,8 @@ create_output_for_connector(struct drm_compositor *ec,
 	const char *type_name;
 	enum output_config config;
 	uint32_t transform;
+	int x, y;
+	struct weston_output *prev = NULL;
 
 	i = find_crtc_for_connector(ec, resources, connector);
 	if (i < 0) {
@@ -1923,6 +1929,14 @@ create_output_for_connector(struct drm_compositor *ec,
 	setup_output_seat_constraint(ec, &output->base, s);
 	free(s);
 
+	weston_config_section_get_string(section, "clone", &s, "");
+	if (strcmp(s, "") != 0) {
+		output->base.is_slave = 1;
+		output->base.master_output_name = strdup(s);
+	}
+	wl_list_init(&output->base.clone_output_list);
+	free(s);
+
 	output->crtc_id = resources->crtcs[i];
 	output->pipe = i;
 	ec->crtc_allocator |= (1 << output->crtc_id);
@@ -2009,9 +2023,27 @@ create_output_for_connector(struct drm_compositor *ec,
 
 	output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
 
-	weston_output_init(&output->base, &ec->base, x, y,
-			   connector->mmWidth, connector->mmHeight,
-			   transform, scale);
+	if (!output->base.is_slave) {
+		if (wl_list_empty(&ec->base.output_list))
+			x = 0;
+		else {
+			prev = container_of(ec->base.output_list.prev,
+					  struct weston_output,
+					  link);
+			x = prev->x + prev->width;
+		}
+		y = 0;
+
+		weston_output_init(&output->base, &ec->base, x, y,
+				   connector->mmWidth, connector->mmHeight,
+				   transform, scale);
+	} else {
+		output->base.compositor = &ec->base;
+		output->transform = transform;
+		output->scale = scale;
+		output->mm_width = connector->mmWidth;
+		output->mm_height = connector->mmHeight;
+	}
 
 	if (ec->use_pixman) {
 		if (drm_output_init_pixman(output, ec) < 0) {
@@ -2034,7 +2066,10 @@ create_output_for_connector(struct drm_compositor *ec,
 		weston_log("Failed to initialize backlight\n");
 	}
 
-	wl_list_insert(ec->base.output_list.prev, &output->base.link);
+	if (!output->base.is_slave)
+		wl_list_insert(ec->base.output_list.prev, &output->base.link);
+	else
+		wl_list_insert(ec->clone_output_list.prev, &output->base.link);
 
 	find_and_parse_output_edid(ec, output, connector);
 	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
@@ -2157,6 +2192,126 @@ destroy_sprites(struct drm_compositor *compositor)
 	}
 }
 
+/* Because slave and master output share the same frame buffer which
+ * is assigned according to master output's mode. If clone output's mode
+ * is larger than frame buffer's size, the mode setting for clone
+ * output will fail. Choosing a nearest mode below master output's mode */
+static int
+adjust_clone_output_mode(struct weston_output *clone_output)
+{
+	struct drm_mode *mode, *choose_mode;
+	uint32_t multiple_delta, choose_delta;
+	struct weston_mode *master_mode;
+
+	choose_mode = NULL;
+	choose_delta = 0xffffffff;
+	master_mode = clone_output->master_output->current_mode;
+
+	wl_list_for_each(mode, &clone_output->mode_list, base.link) {
+		if (mode->mode_info.hdisplay > master_mode->width ||
+		    mode->mode_info.vdisplay > master_mode->height)
+			continue;
+
+		if (mode->mode_info.hdisplay == master_mode->width) {
+			choose_mode = mode;
+			break;
+		} else if (mode->mode_info.vdisplay == master_mode->height) {
+			choose_mode = mode;
+			break;
+		} else {
+			multiple_delta =
+			 (master_mode->width - mode->mode_info.hdisplay) *
+			 (master_mode->height - mode->mode_info.vdisplay);
+			if (multiple_delta < choose_delta) {
+				choose_delta = multiple_delta;
+				choose_mode = mode;
+			}
+		}
+	}
+
+	if (!choose_mode) {
+		weston_log("clone mode fail in chooseing mode for clone output\n");
+		return -1;
+	} else
+		return drm_output_switch_mode(clone_output, &choose_mode->base);
+}
+
+static int
+clone_output_need_adjust_mode(struct weston_output *clone_output)
+{
+	struct weston_output *master_output;
+
+	master_output = clone_output->master_output;
+	if (clone_output->current_mode->height > master_output->current_mode->height ||
+	    clone_output->current_mode->width > master_output->current_mode->width)
+		return 1;
+	else
+		return 0;
+}
+
+static void
+drm_output_add_slave(struct drm_compositor *ec)
+{
+	struct drm_output  *clone_output, *master_output, *tmp;
+	struct weston_output *output;
+	uint32_t found = 0;
+	int x, y;
+
+	/* If no slave output, return.  */
+	if (wl_list_empty(&ec->clone_output_list))
+		return;
+
+	/* Find master output for slave output. */
+	wl_list_for_each_safe(clone_output, tmp,
+			      &ec->clone_output_list, base.link) {
+		found = 0;
+
+		wl_list_remove(&clone_output->base.link);
+
+		wl_list_for_each(master_output,
+				 &ec->base.output_list, base.link) {
+			if (clone_output->base.master_output_name == NULL ||
+			    strncmp(clone_output->base.master_output_name,
+				    master_output->base.name,
+				    strlen(clone_output->base.master_output_name)) != 0)
+				continue;
+
+			clone_output->base.master_output = &master_output->base;
+
+			if (!clone_output_need_adjust_mode(&clone_output->base) ||
+			    adjust_clone_output_mode(&clone_output->base) == 0) {
+				x = master_output->base.x;
+				y = master_output->base.y;
+				found = 1;
+				wl_list_insert(master_output->base.clone_output_list.prev,
+					       &clone_output->base.link);
+			}
+
+			break;
+		}
+
+		if (!found) {
+			if (!wl_list_empty(&ec->base.output_list)) {
+				output = container_of(ec->base.output_list.prev,
+						struct weston_output, link);
+				x = output->x + output->width;
+				y = output->y;
+			} else
+				x = y = 0;
+
+			clone_output->base.is_slave = 0;
+			wl_list_insert(ec->base.output_list.prev,
+					&clone_output->base.link);
+		}
+
+		weston_output_init(&clone_output->base, &ec->base, x, y,
+				   clone_output->mm_width,
+				   clone_output->mm_height,
+				   clone_output->transform,
+				   clone_output->scale);
+	}
+}
+
 static int
 create_outputs(struct drm_compositor *ec, uint32_t option_connector,
 	       struct udev_device *drm_device)
@@ -2164,7 +2319,6 @@ create_outputs(struct drm_compositor *ec, uint32_t option_connector,
 	drmModeConnector *connector;
 	drmModeRes *resources;
 	int i;
-	int x = 0, y = 0;
 
 	resources = drmModeGetResources(ec->drm.fd);
 	if (!resources) {
@@ -2196,20 +2350,18 @@ create_outputs(struct drm_compositor *ec, uint32_t option_connector,
 		    (option_connector == 0 ||
 		     connector->connector_id == option_connector)) {
 			if (create_output_for_connector(ec, resources,
-							connector, x, y,
+							connector,
 							drm_device) < 0) {
 				drmModeFreeConnector(connector);
 				continue;
 			}
-
-			x += container_of(ec->base.output_list.prev,
-					  struct weston_output,
-					  link)->width;
 		}
 
 		drmModeFreeConnector(connector);
 	}
 
+	drm_output_add_slave(ec);
+
 	if (wl_list_empty(&ec->base.output_list)) {
 		weston_log("No currently active connector found.\n");
 		drmModeFreeResources(resources);
@@ -2227,7 +2379,6 @@ update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
 	drmModeConnector *connector;
 	drmModeRes *resources;
 	struct drm_output *output, *next;
-	int x = 0, y = 0;
 	uint32_t connected = 0, disconnects = 0;
 	int i;
 
@@ -2253,18 +2404,7 @@ update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
 		connected |= (1 << connector_id);
 
 		if (!(ec->connector_allocator & (1 << connector_id))) {
-			struct weston_output *last =
-				container_of(ec->base.output_list.prev,
-					     struct weston_output, link);
-
-			/* XXX: not yet needed, we die with 0 outputs */
-			if (!wl_list_empty(&ec->base.output_list))
-				x = last->x + last->width;
-			else
-				x = 0;
-			y = 0;
-			create_output_for_connector(ec, resources,
-						    connector, x, y,
+			create_output_for_connector(ec, resources, connector,
 						    drm_device);
 			weston_log("connector %d connected\n", connector_id);
 
@@ -2756,6 +2896,7 @@ drm_compositor_create(struct wl_display *display,
 	wl_list_init(&ec->sprite_list);
 	create_sprites(ec);
 
+	wl_list_init(&ec->clone_output_list);
 	if (create_outputs(ec, param->connector, drm_device) < 0) {
 		weston_log("failed to create output for %s\n", path);
 		goto err_sprite;
diff --git a/src/compositor.c b/src/compositor.c
index 9de3a90..942c398 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -3082,6 +3082,8 @@ weston_output_destroy(struct weston_output *output)
 	wl_signal_emit(&output->destroy_signal, output);
 
 	free(output->name);
+	if (output->master_output_name)
+		free(output->master_output_name);
 	pixman_region32_fini(&output->region);
 	pixman_region32_fini(&output->previous_damage);
 	output->compositor->output_id_pool &= ~(1 << output->id);
@@ -3615,14 +3617,23 @@ WL_EXPORT void
 weston_compositor_shutdown(struct weston_compositor *ec)
 {
 	struct weston_output *output, *next;
+	struct weston_output *clone_output, *tmp;
 
 	wl_event_source_remove(ec->idle_source);
 	if (ec->input_loop_source)
 		wl_event_source_remove(ec->input_loop_source);
 
 	/* Destroy all outputs associated with this compositor */
-	wl_list_for_each_safe(output, next, &ec->output_list, link)
-		output->destroy(output);
+	wl_list_for_each_safe(output, next, &ec->output_list, link) {
+		/* If master output is destroyed, all associated slave output
+		 * should be destroyed also. */
+		if (!output->is_slave &&
+		    !wl_list_empty(&output->clone_output_list)) {
+			wl_list_for_each_safe(clone_output, tmp,
+					&output->clone_output_list, link)
+			clone_output->destroy(clone_output);
+		}
+	}
 
 	if (ec->renderer)
 		ec->renderer->destroy(ec);
diff --git a/src/compositor.h b/src/compositor.h
index 63e4e2c..549d83d 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -212,6 +212,11 @@ struct weston_output {
 	struct weston_mode *original_mode;
 	struct wl_list mode_list;
 
+	uint32_t is_slave;
+	char *master_output_name;
+	struct weston_output *master_output;
+	struct wl_list clone_output_list;
+
 	void (*start_repaint_loop)(struct weston_output *output);
 	int (*repaint)(struct weston_output *output,
 			pixman_region32_t *damage);
diff --git a/weston.ini.in b/weston.ini.in
index 5181a9e..d8773e7 100644
--- a/weston.ini.in
+++ b/weston.ini.in
@@ -50,11 +50,13 @@ path=@libexecdir@/weston-keyboard
 #mode=1680x1050
 #transform=90
 #icc_profile=/usr/share/color/icc/colord/Bluish.icc
+#clone=VGA1
 
 #[output]
 #name=VGA1
 #mode=173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
 #transform=flipped
+#clone=LVDS1
 
 #[output]
 #name=X1
-- 
1.8.3.2



More information about the wayland-devel mailing list