[PATCH] Add support for surface enter/leave events

Kristian Hoegsberg hoegsberg at gmail.com
Wed Apr 18 09:13:39 PDT 2012


On Tue, Apr 17, 2012 at 05:23:02PM -0400, cdahlin at redhat.com wrote:
> From: Casey Dahlin <cdahlin at redhat.com>

Thanks, that looks good, a lot of the logic is in place here.  I was
thinking that we would use a uint32_t bit mask instead of the array
and the bubble sort.  weston_output needs a new output->id fied,
surface needs an output_mask field and the compositor needs an
output_allocator, like what we do for crtcs and connectors in
compositor-drm.c.  Then to send out the events, compare the
output_bitmask before and after changing surface outputs and send
leave events where the bit goes from 1 to 0 and enter when it goes
from 0 to 1.

Also, we have find_resource_for_surface to look through a list of
wl_resources to the one matching a given client (it needs to be
changed to find_resource_for_client), we should use that to find the
resource when sending out the event.

Kristian

> 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