barriers: Support line and ray barriers

This allows clients to add barriers that extend to the edge of the
screen. Clients are encouraged to use these instead of precise coordinates
in these cases to help prevent pointer leaks.

Signed-off-by: Jasper St. Pierre <jstpierre@mecheye.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Jasper St. Pierre 2012-11-27 15:31:52 -05:00 committed by Peter Hutterer
parent 0a5a0d7c24
commit 707b4dc61f
3 changed files with 67 additions and 7 deletions

View File

@ -140,6 +140,19 @@ barrier_is_blocking_direction(const struct PointerBarrier * barrier,
return (barrier->directions & direction) != direction;
}
static BOOL
inside_segment(int v, int v1, int v2)
{
if (v1 < 0 && v2 < 0) /* line */
return TRUE;
else if (v1 < 0) /* ray */
return v <= v2;
else if (v2 < 0) /* ray */
return v >= v1;
else /* line segment */
return v >= v1 && v <= v2;
}
#define T(v, a, b) (((float)v) - (a)) / ((b) - (a))
#define F(t, a, b) ((t) * ((a) - (b)) + (a))
@ -171,7 +184,7 @@ barrier_is_blocking(const struct PointerBarrier * barrier,
return FALSE;
y = F(t, y1, y2);
if (y < barrier->y1 || y > barrier->y2)
if (!inside_segment(y, barrier->y1, barrier->y2))
return FALSE;
*distance = sqrt((pow(y - y1, 2) + pow(barrier->x1 - x1, 2)));
@ -188,7 +201,7 @@ barrier_is_blocking(const struct PointerBarrier * barrier,
return FALSE;
x = F(t, x1, x2);
if (x < barrier->x1 || x > barrier->x2)
if (!inside_segment(x, barrier->x1, barrier->x2))
return FALSE;
*distance = sqrt((pow(x - x1, 2) + pow(barrier->y1 - y1, 2)));
@ -428,6 +441,18 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
*out_y = y;
}
static void
sort_min_max(INT16 *a, INT16 *b)
{
INT16 A, B;
if (*a < 0 || *b < 0)
return;
A = *a;
B = *b;
*a = min(A, B);
*b = max(A, B);
}
static int
CreatePointerBarrierClient(ClientPtr client,
xXFixesCreatePointerBarrierReq * stuff,
@ -491,10 +516,12 @@ CreatePointerBarrierClient(ClientPtr client,
ret->release_event_id = 0;
ret->hit = FALSE;
ret->seen = FALSE;
ret->barrier.x1 = min(stuff->x1, stuff->x2);
ret->barrier.x2 = max(stuff->x1, stuff->x2);
ret->barrier.y1 = min(stuff->y1, stuff->y2);
ret->barrier.y2 = max(stuff->y1, stuff->y2);
ret->barrier.x1 = stuff->x1;
ret->barrier.x2 = stuff->x2;
ret->barrier.y1 = stuff->y1;
ret->barrier.y2 = stuff->y2;
sort_min_max(&ret->barrier.x1, &ret->barrier.x2);
sort_min_max(&ret->barrier.y1, &ret->barrier.y2);
ret->barrier.directions = stuff->directions & 0x0f;
if (barrier_is_horizontal(&ret->barrier))
ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
@ -587,6 +614,13 @@ XICreatePointerBarrier(ClientPtr client,
if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
return BadValue;
/* no infinite barriers on the wrong axis */
if (barrier_is_horizontal(&b) && (b.y1 < 0 || b.y2 < 0))
return BadValue;
if (barrier_is_vertical(&b) && (b.x1 < 0 || b.x2 < 0))
return BadValue;
if ((err = CreatePointerBarrierClient(client, stuff, &barrier)))
return err;

View File

@ -11,7 +11,7 @@
extern _X_EXPORT RESTYPE PointerBarrierType;
struct PointerBarrier {
CARD16 x1, x2, y1, y2;
INT16 x1, x2, y1, y2;
CARD32 directions;
};

View File

@ -265,6 +265,32 @@ fixes_pointer_barriers_test(void)
x2 = x + 100;
assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
/* ray vert barrier */
barrier.x1 = x;
barrier.x2 = x;
barrier.y1 = -1;
barrier.y2 = y + 100;
/* ray barrier simple case */
y1 = y;
y2 = y;
x1 = x + 50;
x2 = x - 50;
assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
/* endpoint outside y range; should be blocked */
y1 = y - 1000;
y2 = y - 1000;
x1 = x + 50;
x2 = x - 50;
assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
/* endpoint outside y range */
y1 = y + 150;
y2 = y + 150;
x1 = x + 50;
x2 = x - 50;
assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
}
static void