From 14f1112bec18ccece8e732fe6c200a56546230c7 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 17 Mar 2011 13:56:17 -0400 Subject: [PATCH] CRTC confine and pointer barriers --- dix/events.c | 7 + dix/getevents.c | 12 +- include/dix.h | 1 + include/protocol-versions.h | 2 +- mi/mipointer.c | 16 ++- mi/mipointer.h | 6 + randr/randr.c | 2 + randr/randrstr.h | 4 + randr/rrcrtc.c | 155 ++++++++++++++++ test/Makefile.am | 4 +- xfixes/cursor.c | 408 ++++++++++++++++++++++++++++++++++++++++++- xfixes/xfixes.c | 24 ++- xfixes/xfixes.h | 17 ++ xfixes/xfixesint.h | 16 ++ 14 files changed, 658 insertions(+), 16 deletions(-) diff --git a/dix/events.c b/dix/events.c index 07f8b05..d2be84f 100644 --- a/dix/events.c +++ b/dix/events.c @@ -328,6 +328,13 @@ IsMaster(DeviceIntPtr dev) return dev->type == MASTER_POINTER || dev->type == MASTER_KEYBOARD; } +Bool +IsFloating(DeviceIntPtr dev) +{ + return GetMaster(dev, MASTER_KEYBOARD) == NULL; +} + + /** * Max event opcode. */ diff --git a/dix/getevents.c b/dix/getevents.c index 794df42..c66e516 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -812,7 +812,11 @@ accelPointer(DeviceIntPtr dev, int first, int num, int *valuators, CARD32 ms) * miPointerSetPosition() and then scale back into device coordinates (if * needed). miPSP will change x/y if the screen was crossed. * + * The coordinates provided are always absolute. The parameter mode whether + * it was relative or absolute movement that landed us at those coordinates. + * * @param dev The device to be moved. + * @param mode Movement mode (Absolute or Relative) * @param x Pointer to current x-axis value, may be modified. * @param y Pointer to current y-axis value, may be modified. * @param x_frac Fractional part of current x-axis value, may be modified. @@ -824,7 +828,8 @@ accelPointer(DeviceIntPtr dev, int first, int num, int *valuators, CARD32 ms) * @param screeny_frac Fractional part of screen y coordinate, as above. */ static void -positionSprite(DeviceIntPtr dev, int *x, int *y, float x_frac, float y_frac, +positionSprite(DeviceIntPtr dev, int mode, + int *x, int *y, float x_frac, float y_frac, ScreenPtr scr, int *screenx, int *screeny, float *screenx_frac, float *screeny_frac) { int old_screenx, old_screeny; @@ -863,7 +868,7 @@ positionSprite(DeviceIntPtr dev, int *x, int *y, float x_frac, float y_frac, old_screeny = *screeny; /* This takes care of crossing screens for us, as well as clipping * to the current screen. */ - miPointerSetPosition(dev, screenx, screeny); + _miPointerSetPosition(dev, mode, screenx, screeny); if (dev->u.master) { dev->u.master->last.valuators[0] = *screenx; @@ -1193,7 +1198,8 @@ GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons, set_raw_valuators(raw, &mask, raw->valuators.data); - positionSprite(pDev, &x, &y, x_frac, y_frac, scr, &cx, &cy, &cx_frac, &cy_frac); + positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, + &x, &y, x_frac, y_frac, scr, &cx, &cy, &cx_frac, &cy_frac); updateHistory(pDev, &mask, ms); /* Update the valuators with the true value sent to the client*/ diff --git a/include/dix.h b/include/dix.h index 12e4b59..3f99098 100644 --- a/include/dix.h +++ b/include/dix.h @@ -570,6 +570,7 @@ extern Bool _X_EXPORT IsPointerDevice( DeviceIntPtr dev); extern Bool _X_EXPORT IsKeyboardDevice(DeviceIntPtr dev); extern Bool IsPointerEvent(InternalEvent *event); extern _X_EXPORT Bool IsMaster(DeviceIntPtr dev); +extern _X_EXPORT Bool IsFloating(DeviceIntPtr dev); extern _X_HIDDEN void CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master); extern _X_HIDDEN int CorePointerProc(DeviceIntPtr dev, int what); diff --git a/include/protocol-versions.h b/include/protocol-versions.h index 1d33bdd..1dc66ad 100644 --- a/include/protocol-versions.h +++ b/include/protocol-versions.h @@ -126,7 +126,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/mi/mipointer.c b/mi/mipointer.c index 554397a..85f1949 100644 --- a/mi/mipointer.c +++ b/mi/mipointer.c @@ -229,6 +229,10 @@ miPointerSetCursorPosition(DeviceIntPtr pDev, ScreenPtr pScreen, SetupScreen (pScreen); GenerateEvent = generateEvent; + + if (pScreen->ConstrainCursorHarder) + pScreen->ConstrainCursorHarder(pDev, pScreen, Absolute, &x, &y); + /* device dependent - must pend signal and call miPointerWarpCursor */ (*pScreenPriv->screenFuncs->WarpCursor) (pDev, pScreen, x, y); if (!generateEvent) @@ -484,7 +488,7 @@ miPointerMoveNoEvent (DeviceIntPtr pDev, ScreenPtr pScreen, } void -miPointerSetPosition(DeviceIntPtr pDev, int *x, int *y) +_miPointerSetPosition(DeviceIntPtr pDev, int mode, int *x, int *y) { miPointerScreenPtr pScreenPriv; ScreenPtr pScreen; @@ -529,6 +533,9 @@ miPointerSetPosition(DeviceIntPtr pDev, int *x, int *y) if (*y >= pPointer->limits.y2) *y = pPointer->limits.y2 - 1; + if (pScreen->ConstrainCursorHarder) + pScreen->ConstrainCursorHarder(pDev, pScreen, mode, x, y); + if (pPointer->x == *x && pPointer->y == *y && pPointer->pScreen == pScreen) return; @@ -536,6 +543,13 @@ miPointerSetPosition(DeviceIntPtr pDev, int *x, int *y) miPointerMoveNoEvent(pDev, pScreen, *x, *y); } +/* ABI hack */ +void +miPointerSetPosition(DeviceIntPtr pDev, int *x, int *y) +{ + _miPointerSetPosition(pDev, Absolute, x, y); +} + void miPointerGetPosition(DeviceIntPtr pDev, int *x, int *y) { diff --git a/mi/mipointer.h b/mi/mipointer.h index 3c86110..6b6010c 100644 --- a/mi/mipointer.h +++ b/mi/mipointer.h @@ -131,6 +131,12 @@ extern _X_EXPORT void miPointerGetPosition( /* Moves the cursor to the specified position. May clip the co-ordinates: * x and y are modified in-place. */ +extern _X_EXPORT void _miPointerSetPosition( + DeviceIntPtr pDev, + int mode, + int *x, + int *y); + extern _X_EXPORT void miPointerSetPosition( DeviceIntPtr pDev, int *x, diff --git a/randr/randr.c b/randr/randr.c index 6077705..d337129 100644 --- a/randr/randr.c +++ b/randr/randr.c @@ -270,6 +270,8 @@ Bool RRScreenInit(ScreenPtr pScreen) wrap (pScrPriv, pScreen, CloseScreen, RRCloseScreen); + pScreen->ConstrainCursorHarder = RRConstrainCursorHarder; + pScrPriv->numOutputs = 0; pScrPriv->outputs = NULL; pScrPriv->numCrtcs = 0; diff --git a/randr/randrstr.h b/randr/randrstr.h index 7ea6080..d8dd37d 100644 --- a/randr/randrstr.h +++ b/randr/randrstr.h @@ -297,6 +297,7 @@ typedef struct _rrScrPriv { int rate; int size; #endif + Bool discontiguous; } rrScrPrivRec, *rrScrPrivPtr; extern _X_EXPORT DevPrivateKeyRec rrPrivKeyRec; @@ -700,6 +701,9 @@ ProcRRGetPanning (ClientPtr client); int ProcRRSetPanning (ClientPtr client); +void +RRConstrainCursorHarder (DeviceIntPtr, ScreenPtr, int, int *, int *); + /* rrdispatch.c */ extern _X_EXPORT Bool RRClientKnowsRates (ClientPtr pClient); diff --git a/randr/rrcrtc.c b/randr/rrcrtc.c index 98206a2..d4d8f2a 100644 --- a/randr/rrcrtc.c +++ b/randr/rrcrtc.c @@ -1,5 +1,6 @@ /* * Copyright © 2006 Keith Packard + * Copyright 2010 Red Hat, Inc * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -22,6 +23,7 @@ #include "randrstr.h" #include "swaprep.h" +#include "mipointer.h" RESTYPE RRCrtcType; @@ -292,6 +294,92 @@ RRCrtcPendingProperties (RRCrtcPtr crtc) return FALSE; } +static void +crtc_bounds(RRCrtcPtr crtc, int *left, int *right, int *top, int *bottom) +{ + *left = crtc->x; + *top = crtc->y; + + switch (crtc->rotation) { + case RR_Rotate_0: + case RR_Rotate_180: + default: + *right = crtc->x + crtc->mode->mode.width; + *bottom = crtc->y + crtc->mode->mode.height; + return; + case RR_Rotate_90: + case RR_Rotate_270: + *right = crtc->x + crtc->mode->mode.height; + *bottom = crtc->y + crtc->mode->mode.width; + return; + } +} + +/* overlapping counts as adjacent */ +static Bool +crtcs_adjacent(const RRCrtcPtr a, const RRCrtcPtr b) +{ + /* left, right, top, bottom... */ + int al, ar, at, ab; + int bl, br, bt, bb; + int cl, cr, ct, cb; /* the overlap, if any */ + + crtc_bounds(a, &al, &ar, &at, &ab); + crtc_bounds(b, &bl, &br, &bt, &bb); + + cl = max(al, bl); + cr = min(ar, br); + ct = max(at, bt); + cb = min(ab, bb); + + return (cl <= cr) && (ct <= cb); +} + +/* Depth-first search and mark all CRTCs reachable from cur */ +static void +mark_crtcs (rrScrPrivPtr pScrPriv, int *reachable, int cur) +{ + int i; + reachable[cur] = TRUE; + for (i = 0; i < pScrPriv->numCrtcs; ++i) { + if (reachable[i] || !pScrPriv->crtcs[i]->mode) + continue; + if (crtcs_adjacent(pScrPriv->crtcs[cur], pScrPriv->crtcs[i])) + mark_crtcs(pScrPriv, reachable, i); + } +} + +static void +RRComputeContiguity (ScreenPtr pScreen) +{ + rrScrPriv(pScreen); + Bool discontiguous = TRUE; + int i, n = pScrPriv->numCrtcs; + + int *reachable = calloc(n, sizeof(int)); + if (!reachable) + goto out; + + /* Find first enabled CRTC and start search for reachable CRTCs from it */ + for (i = 0; i < n; ++i) { + if (pScrPriv->crtcs[i]->mode) { + mark_crtcs(pScrPriv, reachable, i); + break; + } + } + + /* Check that all enabled CRTCs were marked as reachable */ + for (i = 0; i < n; ++i) + if (pScrPriv->crtcs[i]->mode && !reachable[i]) + goto out; + + discontiguous = FALSE; + +out: + free(reachable); + pScrPriv->discontiguous = discontiguous; +} + /* * Request that the Crtc be reconfigured */ @@ -306,6 +394,7 @@ RRCrtcSet (RRCrtcPtr crtc, { ScreenPtr pScreen = crtc->pScreen; Bool ret = FALSE; + Bool recompute = TRUE; rrScrPriv(pScreen); /* See if nothing changed */ @@ -318,6 +407,7 @@ RRCrtcSet (RRCrtcPtr crtc, !RRCrtcPendingProperties (crtc) && !RRCrtcPendingTransform (crtc)) { + recompute = FALSE; ret = TRUE; } else @@ -381,6 +471,10 @@ RRCrtcSet (RRCrtcPtr crtc, RRPostPendingProperties (outputs[o]); } } + + if (recompute) + RRComputeContiguity(pScreen); + return ret; } @@ -1349,3 +1443,64 @@ ProcRRGetCrtcTransform (ClientPtr client) free(reply); return Success; } + +void +RRConstrainCursorHarder(DeviceIntPtr pDev, ScreenPtr pScreen, int mode, int *x, int *y) +{ + rrScrPriv (pScreen); + int i; + + /* intentional dead space -> let it float */ + if (pScrPriv->discontiguous) + return; + + /* if we're moving inside a crtc, we're fine */ + for (i = 0; i < pScrPriv->numCrtcs; i++) { + RRCrtcPtr crtc = pScrPriv->crtcs[i]; + + int left, right, top, bottom; + + if (!crtc->mode) + continue; + + crtc_bounds(crtc, &left, &right, &top, &bottom); + + if ((*x >= left) && (*x <= right) && (*y >= top) && (*y <= bottom)) + return; + } + + /* if we're trying to escape, clamp to the CRTC we're coming from */ + for (i = 0; i < pScrPriv->numCrtcs; i++) { + RRCrtcPtr crtc = pScrPriv->crtcs[i]; + int nx, ny; + int left, right, top, bottom; + + if (!crtc->mode) + continue; + + crtc_bounds(crtc, &left, &right, &top, &bottom); + miPointerGetPosition(pDev, &nx, &ny); + + if ((nx >= left) && (nx <= right) && (ny >= top) && (ny <= bottom)) { + if ((*x <= left) || (*x >= right)) { + int dx = *x - nx; + + if (dx > 0) + *x = right; + else if (dx < 0) + *x = left; + } + + if ((*y <= top) || (*y >= bottom)) { + int dy = *y - ny; + + if (dy > 0) + *y = bottom; + else if (dy < 0) + *y = top; + } + + return; + } + } +} diff --git a/test/Makefile.am b/test/Makefile.am index 456221e..ccdb859 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ if UNITTESTS SUBDIRS= . xi2 -check_PROGRAMS = xkb input xtest +check_PROGRAMS = xkb input xtest list fixes check_LTLIBRARIES = libxservertest.la TESTS=$(check_PROGRAMS) @@ -16,6 +16,8 @@ endif xkb_LDADD=$(TEST_LDADD) input_LDADD=$(TEST_LDADD) xtest_LDADD=$(TEST_LDADD) +list_LDADD=$(TEST_LDADD) +fixes_LDADD=$(TEST_LDADD) libxservertest_la_LIBADD = \ $(XSERVER_LIBS) \ 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..a57884b 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