Constraining cursor to RandR crtcs
Keith Packard
keithp at keithp.com
Sat Mar 31 16:26:55 PDT 2007
One of the remaining DIX-level issues with RandR is how to keep the
cursor within the area of the root window covered by the crtcs.
It's not quite as easy as with zaphod or xinerama as those both have the
use defining the linkage between monitor edges explicitly while RandR
just places monitors within a single coordinate space, leaving the edge
relationships unspecified.
There are three times when constraints are applied:
1. Cursor motion - the cursor leaves the current monitor. Where
does it go now?
2. WarpPointer - a client moves the cursor directly. How is the
specified position modified?
3. Crtc configuration - the location and size of crtcs changes.
Where does the cursor go now?
I decided to handle 2. and 3. precisely the same way -- just search for
the closest crtc and warp the cursor to the nearest edge. That seems to
work fine and isn't too confusing. The one case I was surprised by at
first was when xrandr disables a crtc before resizing the screen. As the
crtc is disabled, the cursor is warped to another crtc; re-enabling the
crtc in the new configuration doesn't warp it back though. Whatever. I
don't think this is terribly confusing.
Case 1. turned out to be a bit harder; I thought I'd explain my first
plan and then the current plan.
My first thought was that leaving the edge of a monitor should cause the
cursor to move to the nearest crtc in the specified direction, excepting
the crtc just departed. The following layout confused this:
+------+----+
| | |
| b| |
+------+c |
|a |
+----+
Moving the cursor left from 'a' would cause it to pop up to 'b', which
seemed sensible. However, moving down from 'b' would cause it to pop out
again at 'c', which was not at all intuitive. Especially when you drag
left and down from 'a', you end up bouncing between 'b' and 'c'. Quite
nasty.
So, I decided another plan was needed. I decided that the key idea was
to limit the transition from crtc 'A' to crtc 'B' to a single edge.
Classifying the direction from the center of 'A' to the center of 'B'
into four quadrants (left, above, right, below), departures from 'A'
along the matching edge warp the cursor to the closest matching position
in 'B'. Transitions from other edges will not warp to 'B'.
Here's the proposed patch which implements this (the portion in
rrpointer.c is mostly concerned with removing the existing
implementation as it has moved to dix).
The function DisjointRootConfine is called whereever the cursor
constrains are tested to limit cursor positions to visible areas. It
takes two HotSpot pointers, the first indicates the existing position of
the cursor when the cursor is moving under user control, the second the
proposed new cursor location which may be edited by the function.
# On branch refs/heads/server-1.3-branch
# Updated but not checked in:
# (will commit)
#
# modified: dix/events.c
# modified: include/input.h
# modified: randr/rrpointer.c
#
diff --git a/dix/events.c b/dix/events.c
index c57a30e..42d05cd 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -265,6 +265,11 @@ static WindowPtr XYToWindow(
int y
);
+#ifdef RANDR
+static void
+DisjointRootConfine (HotSpot *old, HotSpot *new);
+#endif
+
extern int lastEvent;
static Mask lastEventMask;
@@ -412,6 +417,10 @@ XineramaCheckPhysLimits(
if (sprite.hotShape) /* more work if the shape is a mess */
ConfineToShape(sprite.hotShape, &new.x, &new.y);
+
+#ifdef RANDR
+ DisjointRootConfine(NULL, &new);
+#endif
if((new.x != sprite.hotPhys.x) || (new.y != sprite.hotPhys.y))
{
@@ -454,9 +463,11 @@ XineramaCheckVirtualMotion(
QdEventPtr qe,
WindowPtr pWin
){
-
+ HotSpot old, *old_ptr = NULL;
if (qe)
{
+ old = sprite.hot;
+ old_ptr = &old;
sprite.hot.pScreen = qe->pScreen; /* should always be Screen 0 */
sprite.hot.x = qe->event->u.keyButtonPointer.rootX;
sprite.hot.y = qe->event->u.keyButtonPointer.rootY;
@@ -506,6 +517,10 @@ XineramaCheckVirtualMotion(
if (REGION_NUM_RECTS(&sprite.Reg2) > 1)
ConfineToShape(&sprite.Reg2, &sprite.hot.x, &sprite.hot.y);
+#ifdef RANDR
+ DisjointRootConfine(old_ptr, &sprite.hot);
+#endif
+
if (qe)
{
qe->pScreen = sprite.hot.pScreen;
@@ -547,6 +562,9 @@ XineramaCheckMotion(xEvent *xE)
if (sprite.hotShape)
ConfineToShape(sprite.hotShape, &sprite.hot.x, &sprite.hot.y);
+#ifdef RANDR
+ DisjointRootConfine(&sprite.hotPhys, &sprite.hot);
+#endif
sprite.hotPhys = sprite.hot;
if ((sprite.hotPhys.x != XE_KBPTR.rootX) ||
@@ -762,6 +780,9 @@ CheckPhysLimits(
if (sprite.hotShape)
ConfineToShape(sprite.hotShape, &new.x, &new.y);
#endif
+#ifdef RANDR
+ DisjointRootConfine(&sprite.hotPhys, &new);
+#endif
if ((pScreen != sprite.hotPhys.pScreen) ||
(new.x != sprite.hotPhys.x) || (new.y != sprite.hotPhys.y))
{
@@ -778,6 +799,7 @@ CheckVirtualMotion(
register QdEventPtr qe,
register WindowPtr pWin)
{
+ HotSpot old, *old_ptr = NULL;
#ifdef PANORAMIX
if(!noPanoramiXExtension) {
XineramaCheckVirtualMotion(qe, pWin);
@@ -786,6 +808,8 @@ CheckVirtualMotion(
#endif
if (qe)
{
+ old = sprite.hot;
+ old_ptr = &old;
sprite.hot.pScreen = qe->pScreen;
sprite.hot.x = qe->event->u.keyButtonPointer.rootX;
sprite.hot.y = qe->event->u.keyButtonPointer.rootY;
@@ -814,6 +838,9 @@ CheckVirtualMotion(
if (wBoundingShape(pWin))
ConfineToShape(&pWin->borderSize, &sprite.hot.x, &sprite.hot.y);
#endif
+#ifdef RANDR
+ DisjointRootConfine(old_ptr, &sprite.hot);
+#endif
if (qe)
{
qe->pScreen = sprite.hot.pScreen;
@@ -2020,6 +2047,9 @@ CheckMotion(xEvent *xE)
if (sprite.hotShape)
ConfineToShape(sprite.hotShape, &sprite.hot.x, &sprite.hot.y);
#endif
+#ifdef RANDR
+ DisjointRootConfine(&sprite.hotPhys, &sprite.hot);
+#endif
#ifdef XEVIE
xeviehot.x = sprite.hot.x;
xeviehot.y = sprite.hot.y;
@@ -2303,6 +2333,18 @@ XineramaWarpPointer(ClientPtr client)
y = sprite.physLimits.y2 - 1;
if (sprite.hotShape)
ConfineToShape(sprite.hotShape, &x, &y);
+
+#ifdef RANDR
+ {
+ HotSpot new;
+ new.pScreen = sprite.hotPhys.pScreen;
+ new.x = x;
+ new.y = y;
+ DisjointRootConfine(NULL, &new);
+ x = new.x;
+ y = new.y;
+ }
+#endif
XineramaSetCursorPosition(x, y, TRUE);
@@ -2392,6 +2434,17 @@ ProcWarpPointer(ClientPtr client)
if (sprite.hotShape)
ConfineToShape(sprite.hotShape, &x, &y);
#endif
+#ifdef RANDR
+ {
+ HotSpot new;
+ new.pScreen = sprite.hotPhys.pScreen;
+ new.x = x;
+ new.y = y;
+ DisjointRootConfine(NULL, &new);
+ x = new.x;
+ y = new.y;
+ }
+#endif
(*newScreen->SetCursorPosition)(newScreen, x, y, TRUE);
}
else if (!PointerConfinedToScreen())
@@ -4597,3 +4650,236 @@ WriteEventsToClient(ClientPtr pClient, int count, xEvent *events)
(void)WriteToClient(pClient, count * sizeof(xEvent), (char *) events);
}
}
+
+#ifdef RANDR
+typedef struct _boxes {
+ int num_box;
+ BoxPtr boxes;
+} BoxesRec, *BoxesPtr;
+
+static BoxesPtr root_boxes[MAXSCREENS];
+
+static int
+int_abs (int x)
+{
+ if (x < 0) return -x;
+ return x;
+}
+
+static Bool
+PointInBox (BoxPtr box, int x, int y)
+{
+ return box->x1 <= x && x < box->x2 && box->y1 <= y && y < box->y2;
+}
+
+static BoxPtr
+PointToDisjointBox (BoxesPtr boxes, int x, int y)
+{
+ int b;
+
+ for (b = 0; b < boxes->num_box; b++)
+ if (PointInBox (&boxes->boxes[b], x, y))
+ return &boxes->boxes[b];
+ return NULL;
+}
+
+static xPoint
+BoxToPoint (BoxPtr box, int x, int y)
+{
+ xPoint point;
+
+ if (x < box->x1)
+ point.x = x - box->x1;
+ else if (x >= box->x2)
+ point.x = x - box->x2 + 1;
+ else
+ point.x = 0;
+ if (y < box->y1)
+ point.y = y - box->y1;
+ else if (y >= box->y2)
+ point.y = y - box->y2 + 1;
+ else
+ point.y = 0;
+ return point;
+}
+
+static xPoint
+BoxCenter (BoxPtr a)
+{
+ xPoint point;
+
+ point.x = (a->x1 + a->x2) >> 1;
+ point.y = (a->y1 + a->y2) >> 1;
+ return point;
+}
+
+static xPoint
+BoxToBox (BoxPtr a, BoxPtr b)
+{
+ xPoint ac = BoxCenter (a);
+ xPoint bc = BoxCenter (b);
+ xPoint p;
+
+ p.x = bc.x - ac.x;
+ p.y = bc.y - ac.y;
+ return p;
+}
+
+static int
+Manhattan (xPoint point)
+{
+ return int_abs (point.x) + int_abs (point.y);
+}
+
+static BoxPtr
+PointToNearestDisjointBox (BoxesPtr boxes, int x, int y)
+{
+ int b;
+ BoxPtr nearest;
+ int nearest_dist;
+ int dist;
+
+ nearest = &boxes->boxes[0];
+ nearest_dist = Manhattan (BoxToPoint (nearest, x, y));
+ for (b = 1; b < boxes->num_box; b++)
+ {
+ BoxPtr box = &boxes->boxes[b];
+ dist = Manhattan (BoxToPoint (box, x, y));
+ if (dist < nearest_dist)
+ nearest = box;
+ }
+ return nearest;
+}
+
+typedef enum _quadrant {
+ QCenter, QLeft, QAbove, QRight, QBelow
+} quadrant_t;
+
+static quadrant_t
+quadrant (xPoint point)
+{
+ if (point.x == 0 && point.y == 0)
+ return QCenter;
+
+ if (int_abs (point.x) > int_abs (point.y))
+ {
+ if (point.x < 0)
+ return QLeft;
+ else
+ return QRight;
+ }
+ else
+ {
+ if (point.y < 0)
+ return QAbove;
+ else
+ return QBelow;
+ }
+}
+
+static void
+DisjointRootConfine (HotSpot *old, HotSpot *new)
+{
+ BoxesPtr new_boxes = root_boxes[new->pScreen->myNum];
+ BoxPtr old_box;
+ BoxPtr new_box = NULL;
+
+ if (!new_boxes)
+ return;
+
+ /*
+ * When leaving one box, find the next one in the right direction
+ */
+ if (old &&
+ old->pScreen == new->pScreen &&
+ (old_box = PointToDisjointBox (new_boxes, old->x, old->y)) &&
+ !(new_box = PointToDisjointBox (new_boxes, new->x, new->y)))
+ {
+ int b;
+ quadrant_t qp;
+ int best_dist = 0;
+
+ /*
+ * Figure out which direction we've moved
+ */
+ qp = quadrant (BoxToPoint (old_box, new->x, new->y));
+ for (b = 0; b < new_boxes->num_box; b++)
+ {
+ BoxPtr box = &new_boxes->boxes[b];
+
+ if (box != old_box)
+ {
+ quadrant_t qb = quadrant (BoxToBox (old_box, box));
+
+ if (qp == qb)
+ {
+ int dist = Manhattan (BoxToPoint (box, new->x, new->y));
+ if (!new_box || dist < best_dist)
+ {
+ new_box = box;
+ best_dist = dist;
+ }
+ }
+ }
+ }
+ }
+
+ /* If no box was found above, just confine to the nearest box */
+ if (!new_box)
+ new_box = PointToNearestDisjointBox (new_boxes, new->x, new->y);
+
+ /* confine to target box */
+ if (new->x < new_box->x1)
+ new->x = new_box->x1;
+ else if (new->x >= new_box->x2)
+ new->x = new_box->x2 - 1;
+ if (new->y < new_box->y1)
+ new->y = new_box->y1;
+ else if (new->y >= new_box->y2)
+ new->y = new_box->y2 - 1;
+}
+
+Bool
+SetDisjointRoot (ScreenPtr pScreen, int num_box, BoxPtr boxes)
+{
+ BoxesPtr new_boxes;
+ HotSpot new;
+
+ if (num_box == 0)
+ {
+ if (root_boxes[pScreen->myNum])
+ {
+ xfree (root_boxes[pScreen->myNum]);
+ root_boxes[pScreen->myNum] = NULL;
+ }
+ return TRUE;
+ }
+
+ new_boxes = xalloc (sizeof (BoxesRec) + num_box * sizeof (BoxRec));
+ if (!new_boxes)
+ return FALSE;
+ new_boxes->num_box = num_box;
+ new_boxes->boxes = (BoxPtr) (new_boxes + 1);
+ memcpy (new_boxes->boxes, boxes, num_box * sizeof (BoxRec));
+
+ if (root_boxes[pScreen->myNum])
+ xfree (root_boxes[pScreen->myNum]);
+
+ root_boxes[pScreen->myNum] = new_boxes;
+
+ new = sprite.hotPhys;
+
+ DisjointRootConfine (NULL, &new);
+ if (new.x != sprite.hotPhys.x || new.y !=sprite.hotPhys.y)
+ {
+#ifdef PANORAMIX
+ if(!noPanoramiXExtension)
+ XineramaSetCursorPosition (new.x, new.y, TRUE);
+ else
+#endif
+ (*new.pScreen->SetCursorPosition)(new.pScreen, new.x, new.y, TRUE);
+
+ }
+ return TRUE;
+}
+#endif
diff --git a/include/input.h b/include/input.h
index c0cee24..2599d31 100644
--- a/include/input.h
+++ b/include/input.h
@@ -364,4 +364,7 @@ extern void InitInput(
int /*argc*/,
char ** /*argv*/);
+Bool
+SetDisjointRoot (ScreenPtr pScreen, int num_box, BoxPtr boxes);
+
#endif /* INPUT_H */
diff --git a/randr/rrpointer.c b/randr/rrpointer.c
index 802dcb2..ac778c4 100644
--- a/randr/rrpointer.c
+++ b/randr/rrpointer.c
@@ -23,123 +23,42 @@
#include "randrstr.h"
/*
- * When the pointer moves, check to see if the specified position is outside
- * any of theavailable CRTCs and move it to a 'sensible' place if so, where
- * sensible is the closest monitor to the departing edge.
- *
- * Returns whether the position was adjusted
- */
-
-static Bool
-RRCrtcContainsPosition (RRCrtcPtr crtc, int x, int y)
-{
- RRModePtr mode = crtc->mode;
- int scan_width, scan_height;
-
- if (!mode)
- return FALSE;
-
- RRCrtcGetScanoutSize (crtc, &scan_width, &scan_height);
-
- if (crtc->x <= x && x < crtc->x + scan_width &&
- crtc->y <= y && y < crtc->y + scan_height)
- return TRUE;
- return FALSE;
-}
-
-/*
- * Find the CRTC nearest the specified position, ignoring 'skip'
+ * When the screen is reconfigured, reset the list of
+ * boxes defining this screen
*/
-static void
-RRPointerToNearestCrtc (ScreenPtr pScreen, int x, int y, RRCrtcPtr skip)
-{
- rrScrPriv (pScreen);
- int c;
- RRCrtcPtr nearest = NULL;
- int best = 0;
- int best_dx = 0, best_dy = 0;
-
- for (c = 0; c < pScrPriv->numCrtcs; c++)
- {
- RRCrtcPtr crtc = pScrPriv->crtcs[c];
- RRModePtr mode = crtc->mode;
- int dx, dy;
- int dist;
- int scan_width, scan_height;
-
- if (!mode)
- continue;
- if (crtc == skip)
- continue;
-
- RRCrtcGetScanoutSize (crtc, &scan_width, &scan_height);
-
- if (x < crtc->x)
- dx = crtc->x - x;
- else if (x > crtc->x + scan_width)
- dx = x - (crtc->x + scan_width);
- else
- dx = 0;
- if (y < crtc->y)
- dy = crtc->y - x;
- else if (y > crtc->y + scan_height)
- dy = y - (crtc->y + scan_height);
- else
- dy = 0;
- dist = dx + dy;
- if (!nearest || dist < best)
- {
- nearest = crtc;
- best_dx = dx;
- best_dy = dy;
- }
- }
- if (best_dx || best_dy)
- (*pScreen->SetCursorPosition) (pScreen, x + best_dx, y + best_dy, TRUE);
- pScrPriv->pointerCrtc = nearest;
-}
void
-RRPointerMoved (ScreenPtr pScreen, int x, int y)
+RRPointerScreenConfigured (ScreenPtr pScreen)
{
- rrScrPriv (pScreen);
- RRCrtcPtr pointerCrtc = pScrPriv->pointerCrtc;;
- int c;
+ rrScrPriv(pScreen);
+ BoxPtr boxes = NULL;
+ int c;
+ int nboxes = 0;
- /* Check last known CRTC */
- if (pointerCrtc && RRCrtcContainsPosition (pointerCrtc, x, y))
- return;
-
- /* Check all CRTCs */
- for (c = 0; c < pScrPriv->numCrtcs; c++)
+ if (!pScrPriv) return;
+ if (pScrPriv->numCrtcs)
{
- RRCrtcPtr crtc = pScrPriv->crtcs[c];
-
- if (RRCrtcContainsPosition (crtc, x, y))
- {
- /* Remember containing CRTC */
- pScrPriv->pointerCrtc = crtc;
+ boxes = xalloc (pScrPriv->numCrtcs * sizeof (BoxRec));
+ if (!boxes)
return;
+ for (c = 0; c < pScrPriv->numCrtcs; c++)
+ {
+ RRCrtcPtr crtc = pScrPriv->crtcs[c];
+ int width, height;
+
+ if (crtc->mode)
+ {
+ BoxPtr box = &boxes[nboxes++];
+
+ RRCrtcGetScanoutSize (crtc, &width, &height);
+ box->x1 = crtc->x;
+ box->y1 = crtc->y;
+ box->x2 = crtc->x + width;
+ box->y2 = crtc->y + height;
+ }
}
}
-
- /* None contain pointer, find nearest */
- RRPointerToNearestCrtc (pScreen, x, y, pointerCrtc);
-}
-
-/*
- * When the screen is reconfigured, move the pointer to the nearest
- * CRTC
- */
-void
-RRPointerScreenConfigured (ScreenPtr pScreen)
-{
- WindowPtr pRoot = GetCurrentRootWindow ();
- ScreenPtr pCurrentScreen = pRoot ? pRoot->drawable.pScreen : NULL;
- int x, y;
-
- if (pScreen != pCurrentScreen)
- return;
- GetSpritePosition (&x, &y);
- RRPointerToNearestCrtc (pScreen, x, y, NULL);
+ (void) SetDisjointRoot (pScreen, nboxes, boxes);
+ if (boxes)
+ xfree (boxes);
}
--
keith.packard at intel.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part
URL: <http://lists.x.org/archives/xorg/attachments/20070331/1f0397df/attachment.pgp>
More information about the xorg
mailing list