Blob Blame History Raw
From ad2c48ab05d9415aa384eda6be52576525a3abcf Mon Sep 17 00:00:00 2001
From: Adam Jackson <ajax@redhat.com>
Date: Thu, 17 Mar 2011 13:56:17 -0400
Subject: [PATCH] CRTC confine and pointer barriers

---
 include/protocol-versions.h |    2 +-
 xfixes/cursor.c             |  408 ++++++++++++++++++++++++++++++++++++++++++-
 xfixes/xfixes.c             |   24 ++-
 xfixes/xfixes.h             |   17 ++
 xfixes/xfixesint.h          |   16 ++
 6 files changed, 458 insertions(+), 12 deletions(-)

diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index 8692ded..7b7a9f5 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -122,7 +122,7 @@
 #define SERVER_XF86VIDMODE_MINOR_VERSION	2
 
 /* Fixes */
-#define SERVER_XFIXES_MAJOR_VERSION		4
+#define SERVER_XFIXES_MAJOR_VERSION		5
 #define SERVER_XFIXES_MINOR_VERSION		0
 
 /* X Input */
diff --git a/xfixes/cursor.c b/xfixes/cursor.c
index fb608f6..5c55c95 100644
--- a/xfixes/cursor.c
+++ b/xfixes/cursor.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2010 Red Hat, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -50,13 +51,16 @@
 #include "cursorstr.h"
 #include "dixevents.h"
 #include "servermd.h"
+#include "mipointer.h"
 #include "inputstr.h"
 #include "windowstr.h"
 #include "xace.h"
+#include "list.h"
 
 static RESTYPE		CursorClientType;
 static RESTYPE		CursorHideCountType;
 static RESTYPE		CursorWindowType;
+RESTYPE			PointerBarrierType;
 static CursorPtr	CursorCurrent[MAXDEVICES];
 
 static DevPrivateKeyRec CursorScreenPrivateKeyRec;
@@ -107,6 +111,14 @@ typedef struct _CursorHideCountRec {
     XID			 resource;
 } CursorHideCountRec;
 
+typedef struct PointerBarrierClient *PointerBarrierClientPtr;
+
+struct PointerBarrierClient {
+    ScreenPtr screen;
+    struct PointerBarrier barrier;
+    struct list entry;
+};
+
 /*
  * Wrap DisplayCursor to catch cursor change events
  */
@@ -114,7 +126,9 @@ typedef struct _CursorHideCountRec {
 typedef struct _CursorScreen {
     DisplayCursorProcPtr	DisplayCursor;
     CloseScreenProcPtr		CloseScreen;
+    ConstrainCursorHarderProcPtr ConstrainCursorHarder;
     CursorHideCountPtr          pCursorHideCounts;
+    struct list                 barriers;
 } CursorScreenRec, *CursorScreenPtr;
 
 #define GetCursorScreen(s) ((CursorScreenPtr)dixLookupPrivate(&(s)->devPrivates, CursorScreenPrivateKey))
@@ -184,9 +198,11 @@ CursorCloseScreen (int index, ScreenPtr pScreen)
     Bool		ret;
     CloseScreenProcPtr	close_proc;
     DisplayCursorProcPtr display_proc;
+    ConstrainCursorHarderProcPtr constrain_proc;
 
     Unwrap (cs, pScreen, CloseScreen, close_proc);
     Unwrap (cs, pScreen, DisplayCursor, display_proc);
+    Unwrap (cs, pScreen, ConstrainCursorHarder, constrain_proc);
     deleteCursorHideCountsForScreen(pScreen);
     ret = (*pScreen->CloseScreen) (index, pScreen);
     free(cs);
@@ -1029,6 +1045,391 @@ CursorFreeWindow (pointer data, XID id)
     return 1;
 }
 
+static BOOL
+barrier_is_horizontal(const struct PointerBarrier *barrier)
+{
+    return barrier->y1 == barrier->y2;
+}
+
+static BOOL
+barrier_is_vertical(const struct PointerBarrier *barrier)
+{
+    return barrier->x1 == barrier->x2;
+}
+
+/**
+ * @return The set of barrier movement directions the movement vector
+ * x1/y1 → x2/y2 represents.
+ */
+int
+barrier_get_direction(int x1, int y1, int x2, int y2)
+{
+    int direction = 0;
+
+    /* which way are we trying to go */
+    if (x2 > x1)
+	direction |= BarrierPositiveX;
+    if (x2 < x1)
+	direction |= BarrierNegativeX;
+    if (y2 > y1)
+	direction |= BarrierPositiveY;
+    if (y2 < y1)
+	direction |= BarrierNegativeY;
+
+    return direction;
+}
+
+/**
+ * Test if the barrier may block movement in the direction defined by
+ * x1/y1 → x2/y2. This function only tests whether the directions could be
+ * blocked, it does not test if the barrier actually blocks the movement.
+ *
+ * @return TRUE if the barrier blocks the direction of movement or FALSE
+ * otherwise.
+ */
+BOOL
+barrier_is_blocking_direction(const struct PointerBarrier *barrier, int direction)
+{
+    /* Barriers define which way is ok, not which way is blocking */
+    return (barrier->directions & direction) != direction;
+}
+
+/**
+ * Test if the movement vector x1/y1 → x2/y2 is intersecting with the
+ * barrier. A movement vector with the startpoint or endpoint on the barrier
+ * itself counts as intersecting.
+ *
+ * @param x1 X start coordinate of movement vector
+ * @param y1 Y start coordinate of movement vector
+ * @param x2 X end coordinate of movement vector
+ * @param y2 Y end coordinate of movement vector
+ * @param[out] distance The distance between the start point and the
+ * intersection with the barrier (if applicable).
+ * @return TRUE if the barrier intersects with the given vector
+ */
+BOOL
+barrier_is_blocking(const struct PointerBarrier *barrier,
+		    int x1, int y1, int x2, int y2,
+		    double *distance)
+{
+    BOOL rc = FALSE;
+    float ua, ub, ud;
+    int dir = barrier_get_direction(x1, y1, x2, y2);
+
+    /* Algorithm below doesn't handle edge cases well, hence the extra
+     * checks. */
+    if (barrier_is_vertical(barrier)) {
+	/* handle immediate barrier adjacency, moving away */
+	if (dir & BarrierPositiveX && x1 == barrier->x1)
+	    return FALSE;
+	if (dir & BarrierNegativeX && x1 == (barrier->x1 - 1))
+	    return FALSE;
+	/* startpoint on barrier */
+	if (x1 == barrier->x1 && y1 >= barrier->y1 && y1 <= barrier->y2) {
+	    *distance = 0;
+	    return TRUE;
+	}
+	/* endpoint on barrier */
+	if (x2 == barrier->x1 && y2 >= barrier->y1 && y2 <= barrier->y2) {
+	    *distance = abs(x2 - x1);
+	    return TRUE;
+        }
+    } else {
+	/* handle immediate barrier adjacency, moving away */
+	if (dir & BarrierPositiveY && y1 == barrier->y1)
+	    return FALSE;
+	if (dir & BarrierNegativeY && y1 == (barrier->y1 - 1))
+	    return FALSE;
+	/* startpoint on barrier */
+	if (y1 == barrier->y1 && x1 >= barrier->x1 && x1 <= barrier->x2) {
+	    *distance = 0;
+	    return TRUE;
+        }
+	/* endpoint on barrier */
+	if (y2 == barrier->y1 && x2 >= barrier->x1 && x2 <= barrier->x2) {
+	    *distance = abs(y2 - y1);
+	    return TRUE;
+        }
+    }
+
+    /* not an edge case, compute distance */
+    ua = 0;
+    ud = (barrier->y2 - barrier->y1) * (x2 - x1) - (barrier->x2 - barrier->x1) * (y2 - y1);
+    if (ud != 0) {
+	ua = ((barrier->x2 - barrier->x1) * (y1 - barrier->y1) -
+	     (barrier->y2 - barrier->y1) * (x1 - barrier->x1)) / ud;
+	ub = ((x2 - x1) * (y1 - barrier->y1) -
+	     (y2 - y1) * (x1 - barrier->x1)) / ud;
+	if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
+	    ua = 0;
+    }
+
+    if (ua > 0 && ua <= 1)
+    {
+	double ix = barrier->x1 + ua * (barrier->x2 - barrier->x1);
+	double iy = barrier->y1 + ua * (barrier->y2 - barrier->y1);
+
+	*distance = sqrt(pow(x1 - ix, 2) + pow(y1 - iy, 2));
+	rc = TRUE;
+    }
+
+    return rc;
+}
+
+/**
+ * Find the nearest barrier that is blocking movement from x1/y1 to x2/y2.
+ *
+ * @param dir Only barriers blocking movement in direction dir are checked
+ * @param x1 X start coordinate of movement vector
+ * @param y1 Y start coordinate of movement vector
+ * @param x2 X end coordinate of movement vector
+ * @param y2 Y end coordinate of movement vector
+ * @return The barrier nearest to the movement origin that blocks this movement.
+ */
+static struct PointerBarrier*
+barrier_find_nearest(CursorScreenPtr cs, int dir,
+		     int x1, int y1, int x2, int y2)
+{
+    struct PointerBarrierClient *c;
+    struct PointerBarrier *nearest = NULL;
+    double min_distance = INT_MAX; /* can't get higher than that in X anyway */
+
+    list_for_each_entry(c, &cs->barriers, entry) {
+	struct PointerBarrier *b = &c->barrier;
+	double distance;
+
+	if (!barrier_is_blocking_direction(b, dir))
+	    continue;
+
+	if (barrier_is_blocking(b, x1, y1, x2, y2, &distance))
+	{
+	    if (min_distance > distance)
+	    {
+		min_distance = distance;
+		nearest = b;
+	    }
+	}
+    }
+
+    return nearest;
+}
+
+/**
+ * Clamp to the given barrier given the movement direction specified in dir.
+ *
+ * @param barrier The barrier to clamp to
+ * @param dir The movement direction
+ * @param[out] x The clamped x coordinate.
+ * @param[out] y The clamped x coordinate.
+ */
+void
+barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x, int *y)
+{
+    if (barrier_is_vertical(barrier)) {
+	if ((dir & BarrierNegativeX) & ~barrier->directions)
+	    *x = barrier->x1;
+	if ((dir & BarrierPositiveX) & ~barrier->directions)
+	    *x = barrier->x1 - 1;
+    }
+    if (barrier_is_horizontal(barrier))
+    {
+	if ((dir & BarrierNegativeY) & ~barrier->directions)
+	    *y = barrier->y1;
+	if ((dir & BarrierPositiveY) & ~barrier->directions)
+	    *y = barrier->y1 - 1;
+    }
+}
+
+static void
+CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode, int *x, int *y)
+{
+    CursorScreenPtr cs = GetCursorScreen(screen);
+
+    if (!list_is_empty(&cs->barriers) && !IsFloating(dev) && mode == Relative) {
+	int ox, oy;
+	int dir;
+	struct PointerBarrier *nearest = NULL;
+
+	/* where are we coming from */
+	miPointerGetPosition(dev, &ox, &oy);
+
+	/* How this works:
+	 * Given the origin and the movement vector, get the nearest barrier
+	 * to the origin that is blocking the movement.
+	 * Clamp to that barrier.
+	 * Then, check from the clamped position to the original
+	 * destination, again finding the nearest barrier and clamping.
+	 */
+	dir = barrier_get_direction(ox, oy, *x, *y);
+
+	nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
+	if (nearest) {
+	    barrier_clamp_to_barrier(nearest, dir, x, y);
+
+	    if (barrier_is_vertical(nearest)) {
+		dir &= ~(BarrierNegativeX | BarrierPositiveX);
+		ox = *x;
+	    } else if (barrier_is_horizontal(nearest)) {
+		dir &= ~(BarrierNegativeY | BarrierPositiveY);
+		oy = *y;
+	    }
+
+	    nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
+	    if (nearest) {
+		barrier_clamp_to_barrier(nearest, dir, x, y);
+	    }
+	}
+    }
+
+    if (cs->ConstrainCursorHarder) {
+	screen->ConstrainCursorHarder = cs->ConstrainCursorHarder;
+	screen->ConstrainCursorHarder(dev, screen, mode, x, y);
+	screen->ConstrainCursorHarder = CursorConstrainCursorHarder;
+    }
+}
+
+static struct PointerBarrierClient *
+CreatePointerBarrierClient(ScreenPtr screen, ClientPtr client,
+			   xXFixesCreatePointerBarrierReq *stuff)
+{
+    CursorScreenPtr cs = GetCursorScreen(screen);
+    struct PointerBarrierClient *ret = malloc(sizeof(*ret));
+
+    if (ret) {
+	ret->screen = screen;
+	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.directions = stuff->directions & 0x0f;
+	if (barrier_is_horizontal(&ret->barrier))
+	    ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
+	if (barrier_is_vertical(&ret->barrier))
+	    ret->barrier.directions &= ~(BarrierPositiveY | BarrierNegativeY);
+	list_add(&ret->entry, &cs->barriers);
+    }
+
+    return ret;
+}
+
+int
+ProcXFixesCreatePointerBarrier (ClientPtr client)
+{
+    int err;
+    WindowPtr pWin;
+    struct PointerBarrierClient *barrier;
+    struct PointerBarrier b;
+    REQUEST (xXFixesCreatePointerBarrierReq);
+
+    REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
+    LEGAL_NEW_RESOURCE(stuff->barrier, client);
+
+    err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
+    if (err != Success) {
+	client->errorValue = stuff->window;
+	return err;
+    }
+
+    /* This sure does need fixing. */
+    if (stuff->num_devices)
+	return BadImplementation;
+
+    b.x1 = stuff->x1;
+    b.x2 = stuff->x2;
+    b.y1 = stuff->y1;
+    b.y2 = stuff->y2;
+
+    if (!barrier_is_horizontal(&b) && !barrier_is_vertical(&b))
+	return BadValue;
+
+    /* no 0-sized barriers */
+    if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
+	return BadValue;
+
+    if (!(barrier = CreatePointerBarrierClient(pWin->drawable.pScreen,
+					       client, stuff)))
+	return BadAlloc;
+
+    if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier))
+	return BadAlloc;
+
+    return Success;
+}
+
+int
+SProcXFixesCreatePointerBarrier (ClientPtr client)
+{
+    int n;
+    REQUEST(xXFixesCreatePointerBarrierReq);
+
+    swaps(&stuff->length, n);
+    REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
+    swapl(&stuff->barrier, n);
+    swapl(&stuff->window, n);
+    swaps(&stuff->x1, n);
+    swaps(&stuff->y1, n);
+    swaps(&stuff->x2, n);
+    swaps(&stuff->y2, n);
+    swapl(&stuff->directions, n);
+    return ProcXFixesVector[stuff->xfixesReqType](client);
+}
+
+static int
+CursorFreeBarrier(void *data, XID id)
+{
+    struct PointerBarrierClient *b = NULL, *barrier;
+    ScreenPtr screen;
+    CursorScreenPtr cs;
+
+    barrier = container_of(data, struct PointerBarrierClient, barrier);
+    screen = barrier->screen;
+    cs = GetCursorScreen(screen);
+
+    /* find and unlink from the screen private */
+    list_for_each_entry(b, &cs->barriers, entry) {
+	if (b == barrier) {
+	    list_del(&b->entry);
+	    break;
+	}
+    }
+
+    free(barrier);
+    return Success;
+}
+
+int
+ProcXFixesDestroyPointerBarrier (ClientPtr client)
+{
+    int err;
+    void *barrier;
+    REQUEST (xXFixesDestroyPointerBarrierReq);
+
+    REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq);
+
+    err = dixLookupResourceByType((void **)&barrier, stuff->barrier,
+				  PointerBarrierType, client,
+				  DixDestroyAccess);
+    if (err != Success) {
+	client->errorValue = stuff->barrier;
+	return err;
+    }
+
+    FreeResource(stuff->barrier, RT_NONE);
+    return Success;
+}
+
+int
+SProcXFixesDestroyPointerBarrier (ClientPtr client)
+{
+    int n;
+    REQUEST(xXFixesDestroyPointerBarrierReq);
+
+    swaps(&stuff->length, n);
+    REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq);
+    swapl(&stuff->barrier, n);
+    return ProcXFixesVector[stuff->xfixesReqType](client);
+}
+
 Bool
 XFixesCursorInit (void)
 {
@@ -1048,8 +1449,10 @@ XFixesCursorInit (void)
 	cs = (CursorScreenPtr) calloc(1, sizeof (CursorScreenRec));
 	if (!cs)
 	    return FALSE;
+	list_init(&cs->barriers);
 	Wrap (cs, pScreen, CloseScreen, CursorCloseScreen);
 	Wrap (cs, pScreen, DisplayCursor, CursorDisplayCursor);
+	Wrap (cs, pScreen, ConstrainCursorHarder, CursorConstrainCursorHarder);
 	cs->pCursorHideCounts = NULL;
 	SetCursorScreen (pScreen, cs);
     }
@@ -1059,7 +1462,10 @@ XFixesCursorInit (void)
 						"XFixesCursorHideCount");
     CursorWindowType = CreateNewResourceType(CursorFreeWindow,
 					     "XFixesCursorWindow");
+    PointerBarrierType = CreateNewResourceType(CursorFreeBarrier,
+					      "XFixesPointerBarrier");
 
-    return CursorClientType && CursorHideCountType && CursorWindowType;
+    return CursorClientType && CursorHideCountType && CursorWindowType &&
+	   PointerBarrierType;
 }
 
diff --git a/xfixes/xfixes.c b/xfixes/xfixes.c
index e8c7bf1..de0e4db 100644
--- a/xfixes/xfixes.c
+++ b/xfixes/xfixes.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2010 Red Hat, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -47,10 +48,6 @@
 
 #include "xfixesint.h"
 #include "protocol-versions.h"
-/*
- * Must use these instead of the constants from xfixeswire.h.  They advertise
- * what we implement, not what the protocol headers define.
- */
 
 static unsigned char	XFixesReqCode;
 int		XFixesEventBase;
@@ -97,11 +94,12 @@ ProcXFixesQueryVersion(ClientPtr client)
 
 /* Major version controls available requests */
 static const int version_requests[] = {
-    X_XFixesQueryVersion,	/* before client sends QueryVersion */
-    X_XFixesGetCursorImage,	/* Version 1 */
-    X_XFixesChangeCursorByName,	/* Version 2 */
-    X_XFixesExpandRegion,	/* Version 3 */
-    X_XFixesShowCursor,	        /* Version 4 */
+    X_XFixesQueryVersion,	    /* before client sends QueryVersion */
+    X_XFixesGetCursorImage,	    /* Version 1 */
+    X_XFixesChangeCursorByName,	    /* Version 2 */
+    X_XFixesExpandRegion,	    /* Version 3 */
+    X_XFixesShowCursor,		    /* Version 4 */
+    X_XFixesDestroyPointerBarrier,  /* Version 5 */
 };
 
 #define NUM_VERSION_REQUESTS	(sizeof (version_requests) / sizeof (version_requests[0]))
@@ -142,6 +140,9 @@ int	(*ProcXFixesVector[XFixesNumberRequests])(ClientPtr) = {
 /*************** Version 4 ****************/
     ProcXFixesHideCursor,
     ProcXFixesShowCursor,
+/*************** Version 5 ****************/
+    ProcXFixesCreatePointerBarrier,
+    ProcXFixesDestroyPointerBarrier,
 };
 
 static int
@@ -205,6 +206,9 @@ static int (*SProcXFixesVector[XFixesNumberRequests])(ClientPtr) = {
 /*************** Version 4 ****************/
     SProcXFixesHideCursor,
     SProcXFixesShowCursor,
+/*************** Version 5 ****************/
+    SProcXFixesCreatePointerBarrier,
+    SProcXFixesDestroyPointerBarrier,
 };
 
 static int
@@ -260,6 +264,8 @@ XFixesExtensionInit(void)
 	EventSwapVector[XFixesEventBase + XFixesCursorNotify] =
 	    (EventSwapPtr) SXFixesCursorNotifyEvent;
 	SetResourceTypeErrorValue(RegionResType, XFixesErrorBase + BadRegion);
+	SetResourceTypeErrorValue(PointerBarrierType,
+				  XFixesErrorBase + BadBarrier);
     }
 }
 
diff --git a/xfixes/xfixes.h b/xfixes/xfixes.h
index 1638350..5765e64 100644
--- a/xfixes/xfixes.h
+++ b/xfixes/xfixes.h
@@ -30,6 +30,7 @@
 #include "resource.h"
 
 extern _X_EXPORT RESTYPE RegionResType;
+extern _X_EXPORT RESTYPE PointerBarrierType;
 extern _X_EXPORT int XFixesErrorBase;
 
 #define VERIFY_REGION(pRegion, rid, client, mode)			\
@@ -51,5 +52,21 @@ extern _X_EXPORT int XFixesErrorBase;
 extern _X_EXPORT RegionPtr
 XFixesRegionCopy (RegionPtr pRegion);
 
+struct PointerBarrier {
+    CARD16 x1, x2, y1, y2;
+    CARD32 directions;
+};
+
+
+extern int
+barrier_get_direction(int, int, int, int);
+extern BOOL
+barrier_is_blocking(const struct PointerBarrier*, int, int, int, int, double*);
+extern BOOL
+barrier_is_blocking_direction(const struct PointerBarrier*, int);
+extern void
+barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x, int *y);
+
+
 
 #endif /* _XFIXES_H_ */
diff --git a/xfixes/xfixesint.h b/xfixes/xfixesint.h
index d005369..6ba276e 100644
--- a/xfixes/xfixesint.h
+++ b/xfixes/xfixesint.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2010 Red Hat, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -278,6 +279,21 @@ ProcXFixesShowCursor (ClientPtr client);
 int 
 SProcXFixesShowCursor (ClientPtr client);
 
+/* Version 5 */
+
+int
+ProcXFixesCreatePointerBarrier (ClientPtr client);
+
+int
+SProcXFixesCreatePointerBarrier (ClientPtr client);
+
+int
+ProcXFixesDestroyPointerBarrier (ClientPtr client);
+
+int
+SProcXFixesDestroyPointerBarrier (ClientPtr client);
+
+/* Xinerama */
 extern int (*PanoramiXSaveXFixesVector[XFixesNumberRequests])(ClientPtr);
 void PanoramiXFixesInit (void);
 void PanoramiXFixesReset (void);
-- 
1.7.4.4