[PATCH weston v4 16/20] input: Support non-rectangular pointer confine regions

Pekka Paalanen ppaalanen at gmail.com
Wed Apr 27 13:11:11 UTC 2016


On Tue, 17 Nov 2015 18:11:02 +0800
Jonas Ådahl <jadahl at gmail.com> wrote:

> This patch adds support for when the resulting pointer confinement region
> is not a rectangle.
> 
> Support for this is implemented by converting the rectangles of the
> region into the regions outer border. Pointer motions are then clamped
> to these borders in order to not escape the confinement region.
> 
> Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
> ---
>  src/input.c | 644 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 615 insertions(+), 29 deletions(-)

Hi,

this is a huge patch full of math. When I did a similar thing the last
time, I also wrote clients/cliptest.c to visualize the result. I didn't
write tests, unfortunately.

Would be good to see a visualization or tests for this stuff. Reviewing
the code is a huge task, and having a visual confirmation it works
would help a lot to land it with Tested-bys.

weston-confine is a nice test app, but it's hard to test large motion
vectors where all the code complexity is needed. It wouldn't really
need much more than a way to control two points, start and end of a
motion vector, and then draw the point where the confinement would end
up clamping it to.

Btw. it seems there is a drift when testing on the X11-backend. The
cursor position between weston and X differs the more I run against the
boundary, even if I come back to the middle of the allowed region.
Eventually the pointer will exit the Weston X11 window.

How did you choose this geometrical approach over testing whether each
pixel on the motion vector is inside the region? Can we have large
motion vectors where there would be too many region hit-tests?

Looks like the confinement region is in surface-local coordinates and
integer units, so I think a brute-force hit-testing for each integer
coordinates alone the motion vector might also have been viable.


> 
> diff --git a/src/input.c b/src/input.c
> index 81cdd6f..28b36b0 100644
> --- a/src/input.c
> +++ b/src/input.c
> @@ -32,6 +32,7 @@
>  #include <sys/mman.h>
>  #include <assert.h>
>  #include <unistd.h>
> +#include <values.h>
>  #include <fcntl.h>
>  #include <limits.h>
>  
> @@ -46,6 +47,27 @@ enum pointer_constraint_type {
>  	POINTER_CONSTRAINT_TYPE_CONFINE,
>  };
>  
> +enum motion_direction {
> +	MOTION_DIRECTION_POSITIVE_X = 1 << 0,
> +	MOTION_DIRECTION_NEGATIVE_X = 1 << 1,
> +	MOTION_DIRECTION_POSITIVE_Y = 1 << 2,
> +	MOTION_DIRECTION_NEGATIVE_Y = 1 << 3,
> +};
> +
> +struct vec2d {
> +	double x, y;
> +};
> +
> +struct line {
> +	struct vec2d a;
> +	struct vec2d b;
> +};
> +
> +struct border {
> +	struct line line;
> +	enum motion_direction blocking_dir;
> +};
> +
>  static void
>  maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint);
>  
> @@ -3350,6 +3372,464 @@ confined_pointer_grab_pointer_focus(struct weston_pointer_grab *grab)
>  {
>  }
>  
> +static double
> +vec2d_cross_product(struct vec2d a, struct vec2d b)
> +{
> +	return a.x * b.y - a.y * b.x;
> +}

The first time I see a "2-d cross-product", but yes, it works out
resulting in the z-component from the 3-d one.

> +
> +static struct vec2d
> +vec2d_add(struct vec2d a, struct vec2d b)
> +{
> +	return (struct vec2d) {
> +		.x = a.x + b.x,
> +		.y = a.y + b.y,
> +	};
> +}
> +
> +static struct vec2d
> +vec2d_subtract(struct vec2d a, struct vec2d b)
> +{
> +	return (struct vec2d) {
> +		.x = a.x - b.x,
> +		.y = a.y - b.y,
> +	};
> +}
> +
> +static struct vec2d
> +vec2d_multiply_constant(double c, struct vec2d a)
> +{
> +	return (struct vec2d) {
> +		.x = c * a.x,
> +		.y = c * a.y,
> +	};
> +}
> +
> +static bool
> +lines_intersect(struct line *line1, struct line *line2,
> +		struct vec2d *intersection)
> +{
> +	struct vec2d p = line1->a;
> +	struct vec2d r = vec2d_subtract(line1->b, line1->a);
> +	struct vec2d q = line2->a;
> +	struct vec2d s = vec2d_subtract(line2->b, line2->a);
> +	double rxs;
> +	double sxr;
> +	double t;
> +	double u;
> +
> +	/*
> +	 * The line (p, r) and (q, s) intersects where
> +	 *
> +	 *   p + t r = q + u s
> +	 *
> +	 * Calculate t:
> +	 *
> +	 *   (p + t r) × s = (q + u s) × s
> +	 *   p × s + t (r × s) = q × s + u (s × s)
> +	 *   p × s + t (r × s) = q × s
> +	 *   t (r × s) = q × s - p × s
> +	 *   t (r × s) = (q - p) × s
> +	 *   t = ((q - p) × s) / (r × s)

This looks correct.

> +	 *
> +	 * Using the same method, for u we get:
> +	 *
> +	 *   u = ((p - q) × r) / (s × r)

(p + t r) × r = (q + u s) × r
p × r = q × r + u (s × r)
(p - q) × r = u (s × r)

Correct.

> +	 */
> +
> +	rxs = vec2d_cross_product(r, s);
> +	sxr = vec2d_cross_product(s, r);

r × s = -(s × r), so this could be simplified.

u = ((p - q) × r) / (s × r)
u = ((q - p) × r) / (r × s)

which is actually the exact same formula as for t, except the
cross-multiplier for (q - p).

> +
> +	/* If r × s = 0 then the lines are either parallel or collinear. */
> +	if (fabs(rxs) < DBL_MIN)
> +		return false;
> +
> +	t = vec2d_cross_product(vec2d_subtract(q, p), s) / rxs;
> +	u = vec2d_cross_product(vec2d_subtract(p, q), r) / sxr;
> +
> +	/* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */
> +	if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0)
> +		return false;
> +
> +	*intersection = vec2d_add(p, vec2d_multiply_constant(t, r));
> +	return true;

This looks good.

> +}

That is all I could look at this evening. Up to this point I'd give
reviewed-by, but whole patch for now gets:

Acked-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>

Nothing to see below.


Thanks,
pq


> +
> +static struct border *
> +add_border(struct wl_array *array,
> +	   double x1, double y1,
> +	   double x2, double y2,
> +	   enum motion_direction blocking_dir)
> +{
> +	struct border *border = wl_array_add(array, sizeof *border);
> +
> +	*border = (struct border) {
> +		.line = (struct line) {
> +			.a = (struct vec2d) {
> +				.x = x1,
> +				.y = y1,
> +			},
> +			.b = (struct vec2d) {
> +				.x = x2,
> +				.y = y2,
> +			},
> +		},
> +		.blocking_dir = blocking_dir,
> +	};
> +
> +	return border;
> +}
> +
> +static int
> +compare_lines_x(const void *a, const void *b)
> +{
> +	const struct border *border_a = a;
> +	const struct border *border_b = b;
> +
> +
> +	if (border_a->line.a.x == border_b->line.a.x)
> +		return border_a->line.b.x < border_b->line.b.x;
> +	else
> +		return border_a->line.a.x > border_b->line.a.x;
> +}
> +
> +static void
> +add_non_overlapping_edges(pixman_box32_t *boxes,
> +			  int band_above_start,
> +			  int band_below_start,
> +			  int band_below_end,
> +			  struct wl_array *borders)
> +{
> +	int i;
> +	struct wl_array band_merge;
> +	struct border *border;
> +	struct border *prev_border;
> +	struct border *new_border;
> +
> +	wl_array_init(&band_merge);
> +
> +	/* Add bottom band of previous row, and top band of current row, and
> +	 * sort them so lower left x coordinate comes first. If there are two
> +	 * borders with the same left x coordinate, the wider one comes first.
> +	 */
> +	for (i = band_above_start; i < band_below_start; i++) {
> +		pixman_box32_t *box = &boxes[i];
> +		add_border(&band_merge, box->x1, box->y2, box->x2, box->y2,
> +			   MOTION_DIRECTION_POSITIVE_Y);
> +	}
> +	for (i = band_below_start; i < band_below_end; i++) {
> +		pixman_box32_t *box= &boxes[i];
> +		add_border(&band_merge, box->x1, box->y1, box->x2, box->y1,
> +			   MOTION_DIRECTION_NEGATIVE_Y);
> +	}
> +	qsort(band_merge.data,
> +	      band_merge.size / sizeof *border,
> +	      sizeof *border,
> +	      compare_lines_x);
> +
> +	/* Combine the two combined bands so that any overlapping border is
> +	 * eliminated. */
> +	prev_border = NULL;
> +	wl_array_for_each(border, &band_merge) {
> +		assert(border->line.a.y == border->line.b.y);
> +		assert(!prev_border ||
> +		       prev_border->line.a.y == border->line.a.y);
> +		assert(!prev_border ||
> +		       (prev_border->line.a.x != border->line.a.x ||
> +			prev_border->line.b.x != border->line.b.x));
> +		assert(!prev_border ||
> +		       prev_border->line.a.x <= border->line.a.x);
> +
> +		if (prev_border &&
> +		    prev_border->line.a.x == border->line.a.x) {
> +			/*
> +			 * ------------ +
> +			 * -------      =
> +			 * [     ]-----
> +			 */
> +			prev_border->line.a.x = border->line.b.x;
> +		} else if (prev_border &&
> +			   prev_border->line.b.x == border->line.b.x) {
> +			/*
> +			 * ------------ +
> +			 *       ------ =
> +			 * ------[    ]
> +			 */
> +			prev_border->line.b.x = border->line.a.x;
> +		} else if (prev_border &&
> +			   prev_border->line.b.x == border->line.a.x) {
> +			/*
> +			 * --------        +
> +			 *         ------  =
> +			 * --------------
> +			 */
> +			prev_border->line.b.x = border->line.b.x;
> +		} else if (prev_border &&
> +			   prev_border->line.b.x >= border->line.a.x) {
> +			/*
> +			 * --------------- +
> +			 *      ------     =
> +			 * -----[    ]----
> +			 */
> +			new_border = add_border(borders,
> +						border->line.b.x,
> +						border->line.b.y,
> +						prev_border->line.b.x,
> +						prev_border->line.b.y,
> +						prev_border->blocking_dir);
> +			prev_border->line.b.x = border->line.a.x;
> +			prev_border = new_border;
> +		} else {
> +			assert(!prev_border ||
> +			       prev_border->line.b.x < border->line.a.x);
> +			/*
> +			 * First border or non-overlapping.
> +			 *
> +			 * -----           +
> +			 *        -----    =
> +			 * -----  -----
> +			 */
> +			new_border = wl_array_add(borders, sizeof *border);
> +			*new_border = *border;
> +			prev_border = new_border;
> +		}
> +	}
> +
> +	wl_array_release(&band_merge);
> +}
> +
> +static void
> +add_band_bottom_edges(pixman_box32_t *boxes,
> +		      int band_start,
> +		      int band_end,
> +		      struct wl_array *borders)
> +{
> +	int i;
> +
> +	for (i = band_start; i < band_end; i++) {
> +		add_border(borders,
> +			   boxes[i].x1, boxes[i].y2,
> +			   boxes[i].x2, boxes[i].y2,
> +			   MOTION_DIRECTION_POSITIVE_Y);
> +	}
> +}
> +
> +static void
> +region_to_outline(pixman_region32_t *region, struct wl_array *borders)
> +{
> +	pixman_box32_t *boxes;
> +	int num_boxes;
> +	int i;
> +	int top_most, bottom_most;
> +	int current_roof;
> +	int prev_top;
> +	int band_start, prev_band_start;
> +
> +	/*
> +	 * Remove any overlapping lines from the set of rectangles. Note that
> +	 * pixman regions are grouped as rows of rectangles, where rectangles
> +	 * in one row never touch or overlap and are all of the same height.
> +	 *
> +	 *             -------- ---                   -------- ---
> +	 *             |      | | |                   |      | | |
> +	 *   ----------====---- ---         -----------  ----- ---
> +	 *   |            |            =>   |            |
> +	 *   ----==========---------        -----        ----------
> +	 *       |                 |	        |                 |
> +	 *       -------------------            -------------------
> +	 *
> +	 */
> +
> +	boxes = pixman_region32_rectangles(region, &num_boxes);
> +	prev_top = 0;
> +	top_most = boxes[0].y1;
> +	current_roof = top_most;
> +	bottom_most = boxes[num_boxes - 1].y2;
> +	band_start = 0;
> +	prev_band_start = 0;
> +	for (i = 0; i < num_boxes; i++) {
> +		/* Detect if there is a vertical empty space, and add the lower
> +		 * level of the previous band if so was the case. */
> +		if (i > 0 &&
> +		    boxes[i].y1 != prev_top &&
> +		    boxes[i].y1 != boxes[i - 1].y2) {
> +			current_roof = boxes[i].y1;
> +			add_band_bottom_edges(boxes,
> +					      band_start,
> +					      i,
> +					      borders);
> +		}
> +
> +		/* Special case adding the last band, since it won't be handled
> +		 * by the band change detection below. */
> +		if (boxes[i].y1 != current_roof && i == num_boxes - 1) {
> +			if (boxes[i].y1 != prev_top) {
> +				/* The last band is a single box, so we don't
> +				 * have a prev_band_start to tell us when the
> +				 * previous band started. */
> +				add_non_overlapping_edges(boxes,
> +							  band_start,
> +							  i,
> +							  i + 1,
> +							  borders);
> +			} else {
> +				add_non_overlapping_edges(boxes,
> +							  prev_band_start,
> +							  band_start,
> +							  i + 1,
> +							  borders);
> +			}
> +		}
> +
> +		/* Detect when passing a band and combine the top border of the
> +		 * just passed band with the bottom band of the previous band.
> +		 */
> +		if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top) {
> +			/* Combine the two passed bands. */
> +			if (prev_top != current_roof) {
> +				add_non_overlapping_edges(boxes,
> +							  prev_band_start,
> +							  band_start,
> +							  i,
> +							  borders);
> +			}
> +
> +			prev_band_start = band_start;
> +			band_start = i;
> +		}
> +
> +		/* Add the top border if the box is part of the current roof. */
> +		if (boxes[i].y1 == current_roof) {
> +			add_border(borders,
> +				   boxes[i].x1, boxes[i].y1,
> +				   boxes[i].x2, boxes[i].y1,
> +				   MOTION_DIRECTION_NEGATIVE_Y);
> +		}
> +
> +		/* Add the bottom border of the last band. */
> +		if (boxes[i].y2 == bottom_most) {
> +			add_border(borders,
> +				   boxes[i].x1, boxes[i].y2,
> +				   boxes[i].x2, boxes[i].y2,
> +				   MOTION_DIRECTION_POSITIVE_Y);
> +		}
> +
> +		/* Always add the left border. */
> +		add_border(borders,
> +			   boxes[i].x1, boxes[i].y1,
> +			   boxes[i].x1, boxes[i].y2,
> +			   MOTION_DIRECTION_NEGATIVE_X);
> +
> +		/* Always add the right border. */
> +		add_border(borders,
> +			   boxes[i].x2, boxes[i].y1,
> +			   boxes[i].x2, boxes[i].y2,
> +			   MOTION_DIRECTION_POSITIVE_X);
> +
> +		prev_top = boxes[i].y1;
> +	}
> +}
> +
> +static bool
> +is_border_horizontal (struct border *border)
> +{
> +	return border->line.a.y == border->line.b.y;
> +}
> +
> +static bool
> +is_border_blocking_directions(struct border *border,
> +			      uint32_t directions)
> +{
> +	/* Don't block parallel motions. */
> +	if (is_border_horizontal(border)) {
> +		if ((directions & (MOTION_DIRECTION_POSITIVE_Y |
> +				   MOTION_DIRECTION_NEGATIVE_Y)) == 0)
> +			return false;
> +	} else {
> +		if ((directions & (MOTION_DIRECTION_POSITIVE_X |
> +				   MOTION_DIRECTION_NEGATIVE_X)) == 0)
> +			return false;
> +	}
> +
> +	return (~border->blocking_dir & directions) != directions;
> +}
> +
> +static struct border *
> +get_closest_border(struct wl_array *borders,
> +		   struct line *motion,
> +		   uint32_t directions)
> +{
> +	struct border *border;
> +	struct vec2d intersection;
> +	struct vec2d delta;
> +	double distance_2;
> +	struct border *closest_border = NULL;
> +	double closest_distance_2 = DBL_MAX;
> +
> +	wl_array_for_each(border, borders) {
> +		if (!is_border_blocking_directions(border, directions))
> +			continue;
> +
> +		if (!lines_intersect(&border->line, motion, &intersection))
> +			continue;
> +
> +		delta = vec2d_subtract(intersection, motion->a);
> +		distance_2 = delta.x*delta.x + delta.y*delta.y;
> +		if (distance_2 < closest_distance_2) {
> +			closest_border = border;
> +			closest_distance_2 = distance_2;
> +		}
> +	}
> +
> +	return closest_border;
> +}
> +
> +static void
> +clamp_to_border(struct border *border,
> +		struct line *motion,
> +		uint32_t *motion_dir)
> +{
> +	/*
> +	 * When clamping either rightward or downward motions, the motion needs
> +	 * to be clamped so that the destination coordinate does not end up on
> +	 * the border (see weston_pointer_clamp_event_to_region). Do this by
> +	 * clamping such motions to the border minus the smallest possible
> +	 * wl_fixed_t value.
> +	 */
> +	if (is_border_horizontal(border)) {
> +		if (*motion_dir & MOTION_DIRECTION_POSITIVE_Y)
> +			motion->b.y = border->line.a.y - wl_fixed_to_double(1);
> +		else
> +			motion->b.y = border->line.a.y;
> +		*motion_dir &= ~(MOTION_DIRECTION_POSITIVE_Y |
> +				 MOTION_DIRECTION_NEGATIVE_Y);
> +	} else {
> +		if (*motion_dir & MOTION_DIRECTION_POSITIVE_X)
> +			motion->b.x = border->line.a.x - wl_fixed_to_double(1);
> +		else
> +			motion->b.x = border->line.a.x;
> +		*motion_dir &= ~(MOTION_DIRECTION_POSITIVE_X |
> +				 MOTION_DIRECTION_NEGATIVE_X);
> +	}
> +}
> +
> +static uint32_t
> +get_motion_directions(struct line *motion)
> +{
> +	uint32_t directions = 0;
> +
> +	if (motion->a.x < motion->b.x)
> +		directions |= MOTION_DIRECTION_POSITIVE_X;
> +	else if (motion->a.x > motion->b.x)
> +		directions |= MOTION_DIRECTION_NEGATIVE_X;
> +	if (motion->a.y < motion->b.y)
> +		directions |= MOTION_DIRECTION_POSITIVE_Y;
> +	else if (motion->a.y > motion->b.y)
> +		directions |= MOTION_DIRECTION_NEGATIVE_Y;
> +
> +	return directions;
> +}
> +
>  static void
>  weston_pointer_clamp_event_to_region(struct weston_pointer *pointer,
>  				     struct weston_pointer_motion_event *event,
> @@ -3359,26 +3839,116 @@ weston_pointer_clamp_event_to_region(struct weston_pointer *pointer,
>  {
>  	wl_fixed_t x, y;
>  	wl_fixed_t sx, sy;
> -	wl_fixed_t min_sx = wl_fixed_from_int(region->extents.x1);
> -	wl_fixed_t max_sx = wl_fixed_from_int(region->extents.x2 - 1);
> -	wl_fixed_t max_sy = wl_fixed_from_int(region->extents.y2 - 1);
> -	wl_fixed_t min_sy = wl_fixed_from_int(region->extents.y1);
> +	wl_fixed_t old_sx = pointer->sx;
> +	wl_fixed_t old_sy = pointer->sy;
> +	struct wl_array borders;
> +	struct line motion;
> +	struct border *closest_border;
> +	float new_x_f, new_y_f;
> +	uint32_t directions;
>  
>  	weston_pointer_motion_to_abs(pointer, event, &x, &y);
>  	weston_view_from_global_fixed(pointer->focus, x, y, &sx, &sy);
>  
> -	if (sx < min_sx)
> -		sx = min_sx;
> -	else if (sx > max_sx)
> -		sx = max_sx;
> +	wl_array_init(&borders);
>  
> -	if (sy < min_sy)
> -		sy = min_sy;
> -	else if (sy > max_sy)
> -		sy = max_sy;
> +	/*
> +	 * Generate borders given the confine region we are to use. The borders
> +	 * are defined to be the outer region of the allowed area. This means
> +	 * top/left borders are "within" the allowed area, while bottom/right
> +	 * borders are outside. This needs to be considered when clamping
> +	 * confined motion vectors.
> +	 */
> +	region_to_outline(region, &borders);
> +
> +	motion = (struct line) {
> +		.a = (struct vec2d) {
> +			.x = wl_fixed_to_double(old_sx),
> +			.y = wl_fixed_to_double(old_sy),
> +		},
> +		.b = (struct vec2d) {
> +			.x = wl_fixed_to_double(sx),
> +			.y = wl_fixed_to_double(sy),
> +		},
> +	};
> +	directions = get_motion_directions(&motion);
> +
> +	while (directions) {
> +		closest_border = get_closest_border(&borders,
> +						    &motion,
> +						    directions);
> +		if (closest_border)
> +			clamp_to_border(closest_border, &motion, &directions);
> +		else
> +			break;
> +	}
> +
> +	weston_view_to_global_float(pointer->focus,
> +				    (float) motion.b.x, (float) motion.b.y,
> +				    &new_x_f, &new_y_f);
> +	*clamped_x = wl_fixed_from_double(new_x_f);
> +	*clamped_y = wl_fixed_from_double(new_y_f);
>  
> -	weston_view_to_global_fixed(pointer->focus, sx, sy,
> -				    clamped_x, clamped_y);
> +	wl_array_release(&borders);
> +}
> +
> +static double
> +point_to_border_distance_2(struct border *border, double x, double y)
> +{
> +	double orig_x, orig_y;
> +	double dx, dy;
> +
> +	if (is_border_horizontal(border)) {
> +		if (x < border->line.a.x)
> +			orig_x = border->line.a.x;
> +		else if (x > border->line.b.x)
> +			orig_x = border->line.b.x;
> +		else
> +			orig_x = x;
> +		orig_y = border->line.a.y;
> +	} else {
> +		if (y < border->line.a.y)
> +			orig_y = border->line.a.y;
> +		else if (y > border->line.b.y)
> +			orig_y = border->line.b.y;
> +		else
> +			orig_y = y;
> +		orig_x = border->line.a.x;
> +	}
> +
> +
> +	dx = fabs(orig_x - x);
> +	dy = fabs(orig_y - y);
> +	return dx*dx + dy*dy;
> +}
> +
> +static void
> +warp_to_behind_border(struct border *border, wl_fixed_t *sx, wl_fixed_t *sy)
> +{
> +	switch (border->blocking_dir) {
> +	case MOTION_DIRECTION_POSITIVE_X:
> +	case MOTION_DIRECTION_NEGATIVE_X:
> +		if (border->blocking_dir == MOTION_DIRECTION_POSITIVE_X)
> +			*sx = wl_fixed_from_double(border->line.a.x) - 1;
> +		else
> +			*sx = wl_fixed_from_double(border->line.a.x) + 1;
> +		if (*sy < wl_fixed_from_double(border->line.a.y))
> +			*sy = wl_fixed_from_double(border->line.a.y) + 1;
> +		else if (*sy > wl_fixed_from_double(border->line.b.y))
> +			*sy = wl_fixed_from_double(border->line.b.y) - 1;
> +		break;
> +	case MOTION_DIRECTION_POSITIVE_Y:
> +	case MOTION_DIRECTION_NEGATIVE_Y:
> +		if (border->blocking_dir == MOTION_DIRECTION_POSITIVE_Y)
> +			*sy = wl_fixed_from_double(border->line.a.y) - 1;
> +		else
> +			*sy = wl_fixed_from_double(border->line.a.y) + 1;
> +		if (*sx < wl_fixed_from_double(border->line.a.x))
> +			*sx = wl_fixed_from_double(border->line.a.x) + 1;
> +		else if (*sx > wl_fixed_from_double(border->line.b.x))
> +			*sx = wl_fixed_from_double(border->line.b.x) - 1;
> +		break;
> +	}
>  }
>  
>  static void
> @@ -3396,21 +3966,37 @@ maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint)
>  				      &sy);
>  
>  	if (!is_within_constraint_region(constraint, sx, sy)) {
> -		pixman_region32_t *region = &constraint->region;
> -		wl_fixed_t min_sx = wl_fixed_from_int(region->extents.x1);
> -		wl_fixed_t max_sx = wl_fixed_from_int(region->extents.x2 - 1);
> -		wl_fixed_t max_sy = wl_fixed_from_int(region->extents.y2 - 1);
> -		wl_fixed_t min_sy = wl_fixed_from_int(region->extents.y1);
> -
> -		if (sx < min_sx)
> -			sx = min_sx;
> -		else if (sx > max_sx)
> -			sx = max_sx;
> -
> -		if (sy < min_sy)
> -			sy = min_sy;
> -		else if (sy > max_sy)
> -			sy = max_sy;
> +		double xf = wl_fixed_to_double(sx);
> +		double yf = wl_fixed_to_double(sy);
> +		pixman_region32_t confine_region;
> +		struct wl_array borders;
> +		struct border *border;
> +		double closest_distance_2 = DBL_MAX;
> +		struct border *closest_border = NULL;
> +
> +		wl_array_init(&borders);
> +
> +		pixman_region32_init(&confine_region);
> +		pixman_region32_intersect(&confine_region,
> +					  &constraint->view->surface->input,
> +					  &constraint->region);
> +		region_to_outline(&confine_region, &borders);
> +		pixman_region32_fini(&confine_region);
> +
> +		wl_array_for_each(border, &borders) {
> +			double distance_2;
> +
> +			distance_2 = point_to_border_distance_2(border, xf, yf);
> +			if (distance_2 < closest_distance_2) {
> +				closest_border = border;
> +				closest_distance_2 = distance_2;
> +			}
> +		}
> +		assert(closest_border);
> +
> +		warp_to_behind_border(closest_border, &sx, &sy);
> +
> +		wl_array_release(&borders);
>  
>  		weston_view_to_global_fixed(constraint->view, sx, sy, &x, &y);
>  		weston_pointer_move_to(constraint->pointer, x, y);

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 811 bytes
Desc: OpenPGP digital signature
URL: <https://lists.freedesktop.org/archives/wayland-devel/attachments/20160427/7ed98c25/attachment-0001.sig>


More information about the wayland-devel mailing list