[PATCH] Add support for surface enter/leave events

cdahlin at redhat.com cdahlin at redhat.com
Tue Apr 17 14:23:02 PDT 2012


From: Casey Dahlin <cdahlin at redhat.com>

Signed-off-by: Casey Dahlin <cdahlin at redhat.com>
---
 src/compositor.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/compositor.h |   13 +++++--
 src/shell.c      |   52 +++++++++++++++------------
 3 files changed, 135 insertions(+), 33 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index 17daac4..0f59f46 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -220,7 +220,7 @@ weston_surface_create(struct weston_compositor *compositor)
 	surface->pitch = 1;
 
 	surface->buffer = NULL;
-	surface->output = NULL;
+	surface->output_count = 0;
 
 	pixman_region32_init(&surface->damage);
 	pixman_region32_init(&surface->opaque);
@@ -539,7 +539,7 @@ weston_surface_set_position(struct weston_surface *surface,
 WL_EXPORT int
 weston_surface_is_mapped(struct weston_surface *surface)
 {
-	if (surface->output)
+	if (surface->output_count)
 		return 1;
 	else
 		return 0;
@@ -614,7 +614,7 @@ weston_surface_unmap(struct weston_surface *surface)
 	struct wl_input_device *device = surface->compositor->input_device;
 
 	weston_surface_damage_below(surface);
-	surface->output = NULL;
+	surface->output_count = 0;
 	wl_list_remove(&surface->link);
 	wl_list_remove(&surface->layer_link);
 
@@ -1112,6 +1112,86 @@ surface_destroy(struct wl_client *client, struct wl_resource *resource)
 	wl_resource_destroy(resource);
 }
 
+static inline void
+weston_surface_send_enter(struct weston_surface *es,
+			  struct weston_output *output)
+{
+	struct wl_resource *r;
+
+	wl_list_for_each(r, &output->resource_list, link) {
+		if (r->client != es->surface.resource.client)
+			continue;
+
+		wl_surface_send_enter(&es->surface.resource, r);
+		return;
+	}
+}
+
+static inline void
+weston_surface_send_leave(struct weston_surface *es,
+			  struct weston_output *output)
+{
+	struct wl_resource *r;
+
+	wl_list_for_each(r, &output->resource_list, link) {
+		if (r->client != es->surface.resource.client)
+			continue;
+
+		wl_surface_send_leave(&es->surface.resource, r);
+		return;
+	}
+}
+
+static void
+sort_output_list(struct weston_output **list, size_t count)
+{
+	size_t i, j;
+	struct weston_output *tmp;
+
+	for (i = 1; i < count; i++) {
+		for (j = i; j && list[j] < list[j-1]; j--) {
+			tmp = list[j];
+			list[j] = list[j-1];
+			list[j-1] = tmp;
+		}
+	}
+}
+
+static void
+weston_surface_update_output(struct weston_surface *es,
+			     struct weston_output **outputs, size_t count,
+			     struct weston_output *primary)
+{
+	size_t orig_loc = 0, new_loc = 0;
+
+	sort_output_list(outputs, count);
+	sort_output_list(es->outputs, es->output_count);
+
+	while (orig_loc < es->output_count && new_loc < count) {
+		if (orig_loc == es->output_count ||
+		    es->outputs[orig_loc] > outputs[new_loc]) {
+			weston_surface_send_enter(es, outputs[new_loc++]);
+		} else if (new_loc == count ||
+			 outputs[new_loc] > es->outputs[orig_loc]) {
+			weston_surface_send_leave(es, es->outputs[orig_loc++]);
+		} else {
+			orig_loc++;
+			new_loc++;
+		}
+	}
+
+	es->output_count = count;
+
+	for (orig_loc = 0; orig_loc < count; orig_loc++) {
+		es->outputs[orig_loc] = outputs[orig_loc];
+		if (es->outputs[orig_loc] == primary)
+			new_loc = orig_loc;
+	}
+
+	es->outputs[new_loc] = es->outputs[0];
+	es->outputs[0] = primary;
+}
+
 WL_EXPORT void
 weston_surface_assign_output(struct weston_surface *es)
 {
@@ -1121,6 +1201,9 @@ weston_surface_assign_output(struct weston_surface *es)
 	uint32_t max, area;
 	pixman_box32_t *e;
 
+	struct weston_output *output_list[8];
+	size_t output_count = 0;
+
 	weston_surface_update_transform(es);
 
 	new_output = NULL;
@@ -1133,6 +1216,11 @@ weston_surface_assign_output(struct weston_surface *es)
 		e = pixman_region32_extents(&region);
 		area = (e->x2 - e->x1) * (e->y2 - e->y1);
 
+		if (! area)
+			continue;
+
+		output_list[output_count++] = output;
+
 		if (area >= max) {
 			new_output = output;
 			max = area;
@@ -1140,7 +1228,7 @@ weston_surface_assign_output(struct weston_surface *es)
 	}
 	pixman_region32_fini(&region);
 
-	es->output = new_output;
+	weston_surface_update_output(es, output_list, output_count, new_output);
 	if (!wl_list_empty(&es->frame_callback_list)) {
 		wl_list_insert_list(new_output->frame_callback_list.prev,
 				    &es->frame_callback_list);
@@ -1235,8 +1323,8 @@ surface_frame(struct wl_client *client,
 
 	wl_client_add_resource(client, &cb->resource);
 
-	if (es->output) {
-		wl_list_insert(es->output->frame_callback_list.prev,
+	if (es->output_count) {
+		wl_list_insert(es->outputs[0]->frame_callback_list.prev,
 			       &cb->link);
 	} else {
 		wl_list_insert(es->frame_callback_list.prev, &cb->link);
@@ -2129,6 +2217,8 @@ bind_output(struct wl_client *client,
 	resource = wl_client_add_object(client,
 					&wl_output_interface, NULL, id, data);
 
+	wl_list_insert(&output->resource_list, &resource->link);
+
 	wl_output_send_geometry(resource,
 				output->x,
 				output->y,
@@ -2333,6 +2423,7 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 	weston_output_damage(output);
 
 	wl_list_init(&output->frame_callback_list);
+	wl_list_init(&output->resource_list);
 
 	output->global =
 		wl_display_add_global(c->wl_display, &wl_output_interface,
diff --git a/src/compositor.h b/src/compositor.h
index 93284c5..837ba18 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -74,6 +74,7 @@ enum dpms_enum {
 
 struct weston_output {
 	struct wl_list link;
+	struct wl_list resource_list;
 	struct wl_global *global;
 	struct weston_compositor *compositor;
 	struct weston_matrix matrix;
@@ -321,11 +322,15 @@ struct weston_surface {
 	} transform;
 
 	/*
-	 * Which output to vsync this surface to.
-	 * Used to determine, whether to send or queue frame events.
-	 * Must be NULL, if 'link' is not in weston_compositor::surface_list.
+	 * Outputs this surface is currently within the display region of.
+	 *
+	 * The first output in the list is the primary, which we will to vsync
+	 * this surface to.  Used to determine, whether to send or queue frame
+	 * events.  Count must be 0 if 'link' is not in
+	 * weston_compositor::surface_list.
 	 */
-	struct weston_output *output;
+	struct weston_output *outputs[8]; /* FIXME: Unlimited outputs */
+	size_t output_count;
 
 	struct wl_list frame_callback_list;
 
diff --git a/src/shell.c b/src/shell.c
index 02061c6..8a2fe38 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -509,7 +509,7 @@ shell_surface_set_transient(struct wl_client *client,
 		return;
 
 	/* assign to parents output */
-	shsurf->output = pes->output;
+	shsurf->output = pes->output_count ? pes->outputs[0] : NULL;
  	weston_surface_set_position(es, pes->geometry.x + x,
 					pes->geometry.y + y);
 
@@ -566,12 +566,12 @@ shell_surface_set_maximized(struct wl_client *client,
 	shsurf->saved_position_valid = true;
 
 	shell = shell_surface_get_shell(shsurf);
-	panel_height = get_output_panel_height(shell, es->output);
+	panel_height = get_output_panel_height(shell, es->outputs[0]);
 	edges = WL_SHELL_SURFACE_RESIZE_TOP|WL_SHELL_SURFACE_RESIZE_LEFT;
 
 	wl_shell_surface_send_configure(&shsurf->resource, edges,
-					es->output->current->width,
-					es->output->current->height - panel_height);
+					es->outputs[0]->current->width,
+					es->outputs[0]->current->height - panel_height);
 
 	shsurf->type = SHELL_SURFACE_MAXIMIZED;
 }
@@ -622,7 +622,8 @@ shell_configure_fullscreen(struct shell_surface *shsurf)
 	wl_list_remove(&shsurf->fullscreen.black_surface->layer_link);
 	wl_list_insert(&surface->layer_link,
 		       &shsurf->fullscreen.black_surface->layer_link);
-	shsurf->fullscreen.black_surface->output = output;
+	shsurf->fullscreen.black_surface->outputs[0] = output;
+	shsurf->fullscreen.black_surface->output_count = 1;
 
 	switch (shsurf->fullscreen.type) {
 	case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT:
@@ -777,7 +778,8 @@ shell_map_popup(struct shell_surface *shsurf)
 	struct weston_surface *es = shsurf->surface;
 	struct weston_surface *parent = shsurf->parent->surface;
 
-	es->output = parent->output;
+	es->output_count = parent->output_count;
+	memcpy(es->outputs, parent->outputs, 8 * sizeof(struct weston_output *));
 	shsurf->popup.grab.interface = &popup_grab_interface;
 
 	weston_surface_update_transform(parent);
@@ -998,7 +1000,8 @@ show_screensaver(struct desktop_shell *shell, struct shell_surface *surface)
 
 	wl_list_remove(&surface->surface->layer_link);
 	wl_list_insert(list, &surface->surface->layer_link);
-	surface->surface->output = surface->output;
+	surface->surface->outputs[0] = surface->output;
+	surface->surface->output_count = 1;
 	weston_surface_damage(surface->surface);
 }
 
@@ -1007,7 +1010,7 @@ hide_screensaver(struct desktop_shell *shell, struct shell_surface *surface)
 {
 	wl_list_remove(&surface->surface->layer_link);
 	wl_list_init(&surface->surface->layer_link);
-	surface->surface->output = NULL;
+	surface->surface->output_count = 0;
 }
 
 static void
@@ -1026,7 +1029,7 @@ desktop_shell_set_background(struct wl_client *client,
 
 	wl_list_for_each(priv, &shell->backgrounds, link) {
 		if (priv->output == output_resource->data) {
-			priv->surface->output = NULL;
+			priv->surface->output_count = 0;
 			wl_list_remove(&priv->surface->layer_link);
 			wl_list_remove(&priv->link);
 			break;
@@ -1063,7 +1066,7 @@ desktop_shell_set_panel(struct wl_client *client,
 
 	wl_list_for_each(priv, &shell->panels, link) {
 		if (priv->output == output_resource->data) {
-			priv->surface->output = NULL;
+			priv->surface->output_count = 0;
 			wl_list_remove(&priv->surface->layer_link);
 			wl_list_remove(&priv->link);
 			break;
@@ -1675,9 +1678,9 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
 		break;
 	case SHELL_SURFACE_MAXIMIZED:
 		/* use surface configure to set the geometry */
-		panel_height = get_output_panel_height(shell,surface->output);
-		weston_surface_set_position(surface, surface->output->x,
-					    surface->output->y + panel_height);
+		panel_height = get_output_panel_height(shell,surface->outputs[0]);
+		weston_surface_set_position(surface, surface->outputs[0]->x,
+					    surface->outputs[0]->y + panel_height);
 		break;
 	case SHELL_SURFACE_LOCK:
 		center_on_output(surface, get_default_output(compositor));
@@ -1737,8 +1740,10 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
 
 	if (surface_type != SHELL_SURFACE_NONE) {
 		weston_surface_assign_output(surface);
-		if (surface_type == SHELL_SURFACE_MAXIMIZED)
-			surface->output = shsurf->output;
+		if (surface_type == SHELL_SURFACE_MAXIMIZED) {
+			surface->outputs[0] = shsurf->output;
+			surface->output_count = 1;
+		}
 	}
 
 	switch (surface_type) {
@@ -1788,9 +1793,9 @@ configure(struct desktop_shell *shell, struct weston_surface *surface,
 		break;
 	case SHELL_SURFACE_MAXIMIZED:
 		/* setting x, y and using configure to change that geometry */
-		surface->geometry.x = surface->output->x;
-		surface->geometry.y = surface->output->y +
-			get_output_panel_height(shell,surface->output);
+		surface->geometry.x = surface->outputs[0]->x;
+		surface->geometry.y = surface->outputs[0]->y +
+			get_output_panel_height(shell,surface->outputs[0]);
 		break;
 	case SHELL_SURFACE_TOPLEVEL:
 		break;
@@ -1799,13 +1804,14 @@ configure(struct desktop_shell *shell, struct weston_surface *surface,
 	}
 
 	/* XXX: would a fullscreen surface need the same handling? */
-	if (surface->output) {
+	if (surface->output_count) {
 		weston_surface_assign_output(surface);
 
-		if (surface_type == SHELL_SURFACE_SCREENSAVER)
-			surface->output = shsurf->output;
-		else if (surface_type == SHELL_SURFACE_MAXIMIZED)
-			surface->output = shsurf->output;
+		if (surface_type == SHELL_SURFACE_SCREENSAVER ||
+		    surface_type == SHELL_SURFACE_MAXIMIZED) {
+			surface->outputs[0] = shsurf->output;
+			surface->output_count = 1;
+		}
 	}
 }
 
-- 
1.7.7.6



More information about the wayland-devel mailing list