diff options
author | Kevin E Martin <kem@kem.org> | 2004-06-30 20:06:56 +0000 |
---|---|---|
committer | Kevin E Martin <kem@kem.org> | 2004-06-30 20:06:56 +0000 |
commit | 7976ee51afcad41b611e642d2feb31d805dedcf6 (patch) | |
tree | 218e5c900399e880dd01458154896d011a2ff238 /hw/dmx/dmxcursor.c | |
parent | d5db59bd79f5d8788b99056bf9d969b5b3ad99e1 (diff) |
Add Distributed Multihead X (DMX) support
Diffstat (limited to 'hw/dmx/dmxcursor.c')
-rw-r--r-- | hw/dmx/dmxcursor.c | 922 |
1 files changed, 922 insertions, 0 deletions
diff --git a/hw/dmx/dmxcursor.c b/hw/dmx/dmxcursor.c new file mode 100644 index 000000000..77ec995b9 --- /dev/null +++ b/hw/dmx/dmxcursor.c @@ -0,0 +1,922 @@ +/* $XFree86$ */ +/* + * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Authors: + * David H. Dawes <dawes@xfree86.org> + * Kevin E. Martin <kem@redhat.com> + * Rickard E. (Rik) Faith <faith@redhat.com> + * + */ + +/** \file + * This file contains code than supports cursor movement, including the + * code that initializes and reinitializes the screen positions and + * computes screen overlap. + * + * "This code is based very closely on the XFree86 equivalent + * (xfree86/common/xf86Cursor.c)." --David Dawes. + * + * "This code was then extensively re-written, as explained here." + * --Rik Faith + * + * The code in xf86Cursor.c used edge lists to implement the + * CursorOffScreen function. The edge list computation was complex + * (especially in the face of arbitrarily overlapping screens) compared + * with the speed savings in the CursorOffScreen function. The new + * implementation has erred on the side of correctness, readability, and + * maintainability over efficiency. For the common (non-edge) case, the + * dmxCursorOffScreen function does avoid a loop over all the screens. + * When the cursor has left the screen, all the screens are searched, + * and the first screen (in dmxScreens order) containing the cursor will + * be returned. If run-time profiling shows that this routing is a + * performance bottle-neck, then an edge list may have to be + * reimplemented. An edge list algorithm is O(edges) whereas the new + * algorithm is O(dmxNumScreens). Since edges is usually 1-3 and + * dmxNumScreens may be 30-60 for large backend walls, this trade off + * may be compelling. + * + * The xf86InitOrigins routine uses bit masks during the computation and + * is therefore limited to the length of a word (e.g., 32 or 64 bits) + * screens. Because Xdmx is expected to be used with a large number of + * backend displays, this limitation was removed. The new + * implementation has erred on the side of readability over efficiency, + * using the dmxSL* routines to manage a screen list instead of a + * bitmap, and a function call to decrease the length of the main + * routine. Both algorithms are of the same order, and both are called + * only at server generation time, so trading clarity and long-term + * maintainability for efficiency does not seem justified in this case. + */ + +#define DMX_CURSOR_DEBUG 0 + +#include "dmx.h" +#include "dmxsync.h" +#include "dmxcursor.h" +#include "dmxlog.h" +#include "dmxprop.h" +#include "dmxinput.h" + +#include "mipointer.h" +#include "windowstr.h" +#include "globals.h" +#include "cursorstr.h" +#include "dixevents.h" /* For GetSpriteCursor() */ + +#if DMX_CURSOR_DEBUG +#define DMXDBG0(f) dmxLog(dmxDebug,f) +#define DMXDBG1(f,a) dmxLog(dmxDebug,f,a) +#define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b) +#define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c) +#define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d) +#define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e) +#define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g) +#define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h) +#else +#define DMXDBG0(f) +#define DMXDBG1(f,a) +#define DMXDBG2(f,a,b) +#define DMXDBG3(f,a,b,c) +#define DMXDBG4(f,a,b,c,d) +#define DMXDBG5(f,a,b,c,d,e) +#define DMXDBG6(f,a,b,c,d,e,g) +#define DMXDBG7(f,a,b,c,d,e,g,h) +#endif + +static int dmxCursorDoMultiCursors = 1; + +/** Turn off support for displaying multiple cursors on overlapped + back-end displays. See #dmxCursorDoMultiCursors. */ +void dmxCursorNoMulti(void) +{ + dmxCursorDoMultiCursors = 0; +} + +static Bool dmxCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y) +{ + DMXScreenInfo *dmxScreen; + int i; + int localX = *x; + int localY = *y; + int globalX; + int globalY; + + if (screenInfo.numScreens == 1) return FALSE; + + /* On current screen? */ + dmxScreen = &dmxScreens[(*ppScreen)->myNum]; + if (localX >= 0 + && localX < dmxScreen->rootWidth + && localY >= 0 + && localY < dmxScreen->rootHeight) return FALSE; + + /* Convert to global coordinate space */ + globalX = dmxScreen->rootXOrigin + localX; + globalY = dmxScreen->rootYOrigin + localY; + + /* Is cursor on the current screen? + * This efficiently exits this routine + * for the most common case. */ + if (ppScreen && *ppScreen) { + dmxScreen = &dmxScreens[(*ppScreen)->myNum]; + if (globalX >= dmxScreen->rootXOrigin + && globalX < dmxScreen->rootXOrigin + dmxScreen->rootWidth + && globalY >= dmxScreen->rootYOrigin + && globalY < dmxScreen->rootYOrigin + dmxScreen->rootHeight) + return FALSE; + } + + /* Find first screen cursor is on */ + for (i = 0; i < dmxNumScreens; i++) { + dmxScreen = &dmxScreens[i]; + if (globalX >= dmxScreen->rootXOrigin + && globalX < dmxScreen->rootXOrigin + dmxScreen->rootWidth + && globalY >= dmxScreen->rootYOrigin + && globalY < dmxScreen->rootYOrigin + dmxScreen->rootHeight) { + if (dmxScreen->index == (*ppScreen)->myNum) return FALSE; + *ppScreen = screenInfo.screens[dmxScreen->index]; + *x = globalX - dmxScreen->rootXOrigin; + *y = globalY - dmxScreen->rootYOrigin; + return TRUE; + } + } + return FALSE; +} + +static void dmxCrossScreen(ScreenPtr pScreen, Bool entering) +{ +} + +static void dmxWarpCursor(ScreenPtr pScreen, int x, int y) +{ + DMXDBG3("dmxWarpCursor(%d,%d,%d)\n", pScreen->myNum, x, y); + miPointerWarpCursor(pScreen, x, y); +} + +miPointerScreenFuncRec dmxPointerCursorFuncs = +{ + dmxCursorOffScreen, + dmxCrossScreen, + dmxWarpCursor, + dmxeqEnqueue, + dmxeqSwitchScreen +}; + + +/** Create a list of screens that we'll manipulate. */ +static int *dmxSLCreate(void) +{ + int *list = malloc(dmxNumScreens * sizeof(*list)); + int i; + + for (i = 0; i < dmxNumScreens; i++) list[i] = 1; + return list; +} + +/** Free list. */ +static void dmxSLFree(int *list) +{ + free(list); +} + +/** Find next uninitialized entry in list. */ +static int dmxSLFindNext(int *list) +{ + int i; + for (i = 0; i < dmxNumScreens; i++) if (list[i]) return i; + return -1; +} + +/** Make one pass over all the screens and return the number updated. */ +static int dmxTryComputeScreenOrigins(int *screensLeft) +{ + ScreenPtr pScreen; + DMXScreenInfo *screen; + int i, ref; + int changed = 0; + + for (i = 0; i < dmxNumScreens; i++) { + if (!screensLeft[i]) continue; + screen = &dmxScreens[i]; + switch (screen->where) { + case PosAbsolute: + dixScreenOrigins[i].x = screen->whereX; + dixScreenOrigins[i].y = screen->whereY; + ++changed, screensLeft[i] = 0; + break; + case PosRelative: + ref = screen->whereRefScreen; + if (screensLeft[ref]) break; + dixScreenOrigins[i].x = dixScreenOrigins[ref].x + screen->whereX; + dixScreenOrigins[i].y = dixScreenOrigins[ref].y + screen->whereY; + ++changed, screensLeft[i] = 0; + break; + case PosRightOf: + ref = screen->whereRefScreen; + if (screensLeft[ref]) break; + pScreen = screenInfo.screens[ref]; + dixScreenOrigins[i].x = dixScreenOrigins[ref].x + pScreen->width; + dixScreenOrigins[i].y = dixScreenOrigins[ref].y; + ++changed, screensLeft[i] = 0; + break; + case PosLeftOf: + ref = screen->whereRefScreen; + if (screensLeft[ref]) break; + pScreen = screenInfo.screens[i]; + dixScreenOrigins[i].x = dixScreenOrigins[ref].x - pScreen->width; + dixScreenOrigins[i].y = dixScreenOrigins[ref].y; + ++changed, screensLeft[i] = 0; + break; + case PosBelow: + ref = screen->whereRefScreen; + if (screensLeft[ref]) break; + pScreen = screenInfo.screens[ref]; + dixScreenOrigins[i].x = dixScreenOrigins[ref].x; + dixScreenOrigins[i].y = dixScreenOrigins[ref].y + pScreen->height; + ++changed, screensLeft[i] = 0; + break; + case PosAbove: + ref = screen->whereRefScreen; + if (screensLeft[ref]) break; + pScreen = screenInfo.screens[i]; + dixScreenOrigins[i].x = dixScreenOrigins[ref].x; + dixScreenOrigins[i].y = dixScreenOrigins[ref].y - pScreen->height; + ++changed, screensLeft[i] = 0; + break; + case PosNone: + dmxLog(dmxFatal, "No position information for screen %d\n", i); + } + } + return changed; +} + +static void dmxComputeScreenOrigins(void) +{ + int *screensLeft; + int i, ref; + int minX, minY; + + /* Compute origins based on + * configuration information. */ + screensLeft = dmxSLCreate(); + while ((i = dmxSLFindNext(screensLeft)) >= 0) { + while (dmxTryComputeScreenOrigins(screensLeft)); + if ((i = dmxSLFindNext(screensLeft)) >= 0) { + /* All of the remaining screens are referencing each other. + * Assign a value to one of them and go through again. This + * guarantees that we will eventually terminate. + */ + ref = dmxScreens[i].whereRefScreen; + dixScreenOrigins[ref].x = dixScreenOrigins[ref].y = 0; + screensLeft[ref] = 0; + } + } + dmxSLFree(screensLeft); + + + /* Justify the topmost and leftmost to + * (0,0). */ + minX = dixScreenOrigins[0].x; + minY = dixScreenOrigins[0].y; + for (i = 1; i < dmxNumScreens; i++) { /* Compute minX, minY */ + if (dixScreenOrigins[i].x < minX) minX = dixScreenOrigins[i].x; + if (dixScreenOrigins[i].y < minY) minY = dixScreenOrigins[i].y; + } + if (minX || minY) { + for (i = 0; i < dmxNumScreens; i++) { + dixScreenOrigins[i].x -= minX; + dixScreenOrigins[i].y -= minY; + } + } +} + +/** Recompute origin information in the #dmxScreens list. This is + * either called from #dmxInitOrigins() or from #dmxReconfig(). */ +void dmxReInitOrigins(void) +{ + int i; + + if (dmxNumScreens > MAXSCREENS) + dmxLog(dmxFatal, "dmxNumScreens = %d > MAXSCREENS = %d\n", + dmxNumScreens, MAXSCREENS); + + for (i = 0; i < dmxNumScreens; i++) { + DMXScreenInfo *dmxScreen = &dmxScreens[i]; + dmxLogOutput(dmxScreen, + "s=%dx%d%+d%+d r=%dx%d%+d%+d @%d,%d" + " (be=%dx%d depth=%d bpp=%d)\n", + dmxScreen->scrnWidth, dmxScreen->scrnHeight, + dmxScreen->scrnX, dmxScreen->scrnY, + + dmxScreen->rootWidth, dmxScreen->rootHeight, + dmxScreen->rootX, dmxScreen->rootY, + + dmxScreen->rootXOrigin, dmxScreen->rootYOrigin, + dmxScreen->beWidth, dmxScreen->beHeight, + dmxScreen->beDepth, dmxScreen->beBPP); + } +} + +/** Initialize screen origins (and relative position). This is called + * for each server generation. For dynamic reconfiguration, use + * #dmxReInitOrigins() instead. */ +void dmxInitOrigins(void) +{ + int i; + + if (dmxNumScreens > MAXSCREENS) + dmxLog(dmxFatal, "dmxNumScreens = %d > MAXSCREENS = %d\n", + dmxNumScreens, MAXSCREENS); + + for (i = 0; i < dmxNumScreens; i++) { + DMXScreenInfo *dmxScreen = &dmxScreens[i]; + dmxLogOutput(dmxScreen, + "(request) s=%dx%d%+d%+d r=%dx%d%+d%+d @%d,%d (%d)" + " (be=%dx%d depth=%d bpp=%d)\n", + dmxScreen->scrnWidth, dmxScreen->scrnHeight, + dmxScreen->scrnX, dmxScreen->scrnY, + + dmxScreen->rootWidth, dmxScreen->rootHeight, + dmxScreen->rootX, dmxScreen->rootY, + + dmxScreen->whereX, dmxScreen->whereY, + dmxScreen->where, + + dmxScreen->beWidth, dmxScreen->beHeight, + dmxScreen->beDepth, dmxScreen->beBPP); + } + + dmxComputeScreenOrigins(); + + for (i = 0; i < dmxNumScreens; i++) { + DMXScreenInfo *dmxScreen = &dmxScreens[i]; + dmxScreen->rootXOrigin = dixScreenOrigins[i].x; + dmxScreen->rootYOrigin = dixScreenOrigins[i].y; + } + + dmxReInitOrigins(); +} + +/** Returns non-zero if the global \a x, \a y coordinate is on the + * screen window of the \a dmxScreen. */ +int dmxOnScreen(int x, int y, DMXScreenInfo *dmxScreen) +{ +#if DMX_CURSOR_DEBUG > 1 + dmxLog(dmxDebug, + "dmxOnScreen %d %d,%d (r=%dx%d%+d%+d@%d,%d s=%dx%d%+d%+d)\n", + dmxScreen->index, x, y, + dmxScreen->rootWidth, dmxScreen->rootHeight, + dmxScreen->rootX, dmxScreen->rootY, + dmxScreen->rootXOrigin, dmxScreen->rootYOrigin, + dmxScreen->scrnWidth, dmxScreen->scrnHeight, + dmxScreen->scrnX, dmxScreen->scrnY); +#endif + if (x >= dmxScreen->rootXOrigin + && x < dmxScreen->rootXOrigin + dmxScreen->rootWidth + && y >= dmxScreen->rootYOrigin + && y < dmxScreen->rootYOrigin + dmxScreen->rootHeight) return 1; + return 0; +} + +/** Returns non-zero if \a a overlaps \a b. */ +static int dmxDoesOverlap(DMXScreenInfo *a, DMXScreenInfo *b) +{ + if (dmxOnScreen(a->rootXOrigin, + a->rootYOrigin, b)) return 1; + + if (dmxOnScreen(a->rootXOrigin, + a->rootYOrigin + a->scrnWidth, b)) return 1; + + if (dmxOnScreen(a->rootXOrigin + a->scrnHeight, + a->rootYOrigin, b)) return 1; + + if (dmxOnScreen(a->rootXOrigin + a->scrnHeight, + a->rootYOrigin + a->scrnWidth, b)) return 1; + + if (dmxOnScreen(b->rootXOrigin, + b->rootYOrigin, a)) return 1; + + if (dmxOnScreen(b->rootXOrigin, + b->rootYOrigin + b->scrnWidth, a)) return 1; + + if (dmxOnScreen(b->rootXOrigin + b->scrnHeight, + b->rootYOrigin, a)) return 1; + + if (dmxOnScreen(b->rootXOrigin + b->scrnHeight, + b->rootYOrigin + b->scrnWidth, a)) return 1; + + return 0; +} + +/** Used with #dmxInterateOverlap to print out a list of screens which + * overlap each other. */ +static void *dmxPrintOverlap(DMXScreenInfo *dmxScreen, void *closure) +{ + DMXScreenInfo *a = closure; + if (dmxScreen != a) { + if (dmxScreen->cursorNotShared) + dmxLogOutputCont(a, " [%d/%s]", dmxScreen->index, dmxScreen->name); + else + dmxLogOutputCont(a, " %d/%s", dmxScreen->index, dmxScreen->name); + } + return NULL; +} + +/** Iterate over the screens which overlap with the \a start screen, + * calling \a f with the \a closure for each argument. Often used with + * #dmxPrintOverlap. */ +static void *dmxIterateOverlap(DMXScreenInfo *start, + void *(*f)(DMXScreenInfo *dmxScreen, void *), + void *closure) +{ + DMXScreenInfo *pt; + + if (!start->over) return f(start, closure); + + for (pt = start->over; /* condition at end of loop */; pt = pt->over) { + void *retval; + if ((retval = f(pt, closure))) return retval; + if (pt == start) break; + } + return NULL; +} + +/** Used with #dmxPropertyIterate to determine if screen \a a is the + * same as the screen \a closure. */ +static void *dmxTestSameDisplay(DMXScreenInfo *a, void *closure) +{ + DMXScreenInfo *b = closure; + + if (a == b) return a; + return NULL; +} + +/** Detects overlapping dmxScreens and creates circular lists. This + * uses an O(dmxNumScreens^2) algorithm, but dmxNumScreens is < 100 and + * the computation only needs to be performed for every server + * generation or dynamic reconfiguration . */ +void dmxInitOverlap(void) +{ + int i, j; + DMXScreenInfo *a, *b, *pt; + + for (i = 0; i < dmxNumScreens; i++) dmxScreens[i].over = NULL; + + for (i = 0; i < dmxNumScreens; i++) { + a = &dmxScreens[i]; + + for (j = i+1; j < dmxNumScreens; j++) { + b = &dmxScreens[j]; + if (b->over) continue; + + if (dmxDoesOverlap(a, b)) { + DMXDBG6("%d overlaps %d: a=%p %p b=%p %p\n", + a->index, b->index, a, a->over, b, b->over); + b->over = (a->over ? a->over : a); + a->over = b; + } + } + } + + for (i = 0; i < dmxNumScreens; i++) { + a = &dmxScreens[i]; + + if (!a->over) continue; + + /* Flag all pairs that are on same display */ + for (pt = a->over; pt != a; pt = pt->over) { + if (dmxPropertyIterate(a, dmxTestSameDisplay, pt)) { + /* The ->over sets contain the transitive set of screens + * that overlap. For screens that are on the same + * backend display, we only want to exclude pairs of + * screens that mutually overlap on the backend display, + * so we call dmxDoesOverlap, which is stricter than the + * ->over set. */ + if (!dmxDoesOverlap(a, pt)) continue; + a->cursorNotShared = 1; + pt->cursorNotShared = 1; + dmxLog(dmxInfo, + "Screen %d and %d overlap on %s\n", + a->index, pt->index, a->name); + } + } + } + + for (i = 0; i < dmxNumScreens; i++) { + a = &dmxScreens[i]; + + if (a->over) { + dmxLogOutput(a, "Overlaps"); + dmxIterateOverlap(a, dmxPrintOverlap, a); + dmxLogOutputCont(a, "\n"); + } + } +} + +/** Create \a pCursor on the back-end associated with \a pScreen. */ +void dmxBECreateCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + dmxCursorPrivPtr pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); + CursorBitsPtr pBits = pCursor->bits; + Pixmap src, msk; + XColor fg, bg; + XImage *img; + XlibGC gc = NULL; + XGCValues v; + unsigned long m; + int i; + + if (!pCursorPriv) + return; + + m = GCFunction | GCPlaneMask | GCForeground | GCBackground | GCClipMask; + v.function = GXcopy; + v.plane_mask = AllPlanes; + v.foreground = 1L; + v.background = 0L; + v.clip_mask = None; + + for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) { + if (dmxScreen->bePixmapFormats[i].depth == 1) { + /* Create GC in the back-end servers */ + gc = XCreateGC(dmxScreen->beDisplay, dmxScreen->scrnDefDrawables[i], + m, &v); + break; + } + } + if (!gc) + dmxLog(dmxFatal, "dmxRealizeCursor: gc not initialized\n"); + + src = XCreatePixmap(dmxScreen->beDisplay, dmxScreen->scrnWin, + pBits->width, pBits->height, 1); + msk = XCreatePixmap(dmxScreen->beDisplay, dmxScreen->scrnWin, + pBits->width, pBits->height, 1); + + img = XCreateImage(dmxScreen->beDisplay, + dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual, + 1, XYBitmap, 0, (char *)pBits->source, + pBits->width, pBits->height, + BitmapPad(dmxScreen->beDisplay), 0); + + XPutImage(dmxScreen->beDisplay, src, gc, img, 0, 0, 0, 0, + pBits->width, pBits->height); + + XFree(img); + + img = XCreateImage(dmxScreen->beDisplay, + dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual, + 1, XYBitmap, 0, (char *)pBits->mask, + pBits->width, pBits->height, + BitmapPad(dmxScreen->beDisplay), 0); + + XPutImage(dmxScreen->beDisplay, msk, gc, img, 0, 0, 0, 0, + pBits->width, pBits->height); + + XFree(img); + + fg.red = pCursor->foreRed; + fg.green = pCursor->foreGreen; + fg.blue = pCursor->foreBlue; + + bg.red = pCursor->backRed; + bg.green = pCursor->backGreen; + bg.blue = pCursor->backBlue; + + pCursorPriv->cursor = XCreatePixmapCursor(dmxScreen->beDisplay, + src, msk, + &fg, &bg, + pBits->xhot, pBits->yhot); + + XFreePixmap(dmxScreen->beDisplay, src); + XFreePixmap(dmxScreen->beDisplay, msk); + XFreeGC(dmxScreen->beDisplay, gc); + + dmxSync(dmxScreen, FALSE); +} + +static Bool _dmxRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + dmxCursorPrivPtr pCursorPriv; + + DMXDBG2("_dmxRealizeCursor(%d,%p)\n", pScreen->myNum, pCursor); + + pCursor->devPriv[pScreen->myNum] = xalloc(sizeof(*pCursorPriv)); + if (!pCursor->devPriv[pScreen->myNum]) + return FALSE; + + pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); + pCursorPriv->cursor = (Cursor)0; + + if (!dmxScreen->beDisplay) + return TRUE; + + dmxBECreateCursor(pScreen, pCursor); + return TRUE; +} + +/** Free \a pCursor on the back-end associated with \a pScreen. */ +Bool dmxBEFreeCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + dmxCursorPrivPtr pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); + + if (pCursorPriv) { + XFreeCursor(dmxScreen->beDisplay, pCursorPriv->cursor); + pCursorPriv->cursor = (Cursor)0; + return TRUE; + } + + return FALSE; +} + +static Bool _dmxUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + + DMXDBG3("_dmxUnrealizeCursor(%d,%p) %p\n", + pScreen->myNum, pCursor, pCursorPriv); + + if (dmxScreen->beDisplay) { + if (dmxBEFreeCursor(pScreen, pCursor)) + xfree(pCursor->devPriv[pScreen->myNum]); + } + pCursor->devPriv[pScreen->myNum] = NULL; + + return TRUE; +} + +static void _dmxMoveCursor(ScreenPtr pScreen, int x, int y) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + int newX = x + dmxScreen->rootX; + int newY = y + dmxScreen->rootY; + + if (newX < 0) newX = 0; + if (newY < 0) newY = 0; + + DMXDBG5("_dmxMoveCursor(%d,%d,%d) -> %d,%d\n", + pScreen->myNum, x, y, newX, newY); + if (dmxScreen->beDisplay) { + XWarpPointer(dmxScreen->beDisplay, None, dmxScreen->scrnWin, + 0, 0, 0, 0, newX, newY); + dmxSync(dmxScreen, TRUE); + } +} + +static void _dmxSetCursor(ScreenPtr pScreen, CursorPtr pCursor, int x, int y) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + + DMXDBG4("_dmxSetCursor(%d,%p,%d,%d)\n", pScreen->myNum, pCursor, x, y); + + if (pCursor) { + dmxCursorPrivPtr pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen); + if (dmxScreen->curCursor != pCursorPriv->cursor) { + if (dmxScreen->beDisplay) + XDefineCursor(dmxScreen->beDisplay, dmxScreen->scrnWin, + pCursorPriv->cursor); + dmxScreen->cursor = pCursor; + dmxScreen->curCursor = pCursorPriv->cursor; + dmxScreen->cursorVisible = 1; + } + _dmxMoveCursor(pScreen, x, y); + } else { + if (dmxScreen->beDisplay) + XDefineCursor(dmxScreen->beDisplay, dmxScreen->scrnWin, + dmxScreen->noCursor); + dmxScreen->cursor = NULL; + dmxScreen->curCursor = (Cursor)0; + dmxScreen->cursorVisible = 0; + } + if (dmxScreen->beDisplay) dmxSync(dmxScreen, TRUE); +} + +static Bool dmxRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; + DMXScreenInfo *pt; + + if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) + return _dmxRealizeCursor(pScreen, pCursor); + + for (pt = start->over; /* condition at end of loop */; pt = pt->over) { + if (pt->cursorNotShared) continue; + _dmxRealizeCursor(screenInfo.screens[pt->index], pCursor); + if (pt == start) break; + } + return TRUE; +} + +static Bool dmxUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; + DMXScreenInfo *pt; + + if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) + return _dmxUnrealizeCursor(pScreen, pCursor); + + for (pt = start->over; /* condition at end of loop */; pt = pt->over) { + if (pt->cursorNotShared) continue; + _dmxUnrealizeCursor(screenInfo.screens[pt->index], pCursor); + if (pt == start) break; + } + return TRUE; +} + +static CursorPtr dmxFindCursor(DMXScreenInfo *start) +{ + DMXScreenInfo *pt; + + if (!start || !start->over) return GetSpriteCursor(); + for (pt = start->over; /* condition at end of loop */; pt = pt->over) { + if (pt->cursor) return pt->cursor; + if (pt == start) break; + } + return GetSpriteCursor(); +} + +/** Move the cursor to coordinates (\a x, \a y)on \a pScreen. This + * function is usually called via #dmxPointerSpriteFuncs, except during + * reconfiguration when the cursor is repositioned to force an update on + * newley overlapping screens and on screens that no longer overlap. */ +void dmxMoveCursor(ScreenPtr pScreen, int x, int y) +{ + DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; + DMXScreenInfo *pt; + + DMXDBG3("dmxMoveCursor(%d,%d,%d)\n", pScreen->myNum, x, y); + + if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) { + _dmxMoveCursor(pScreen, x, y); + return; + } + + for (pt = start->over; /* condition at end of loop */; pt = pt->over) { + if (pt->cursorNotShared) continue; + if (dmxOnScreen(x + start->rootXOrigin, y + start->rootYOrigin, pt)) { + if (/* pt != start && */ !pt->cursorVisible) { + if (!pt->cursor) { + /* This only happens during + * reconfiguration when a new overlap + * occurs. */ + CursorPtr pCursor; + + if ((pCursor = dmxFindCursor(start))) + _dmxRealizeCursor(screenInfo.screens[pt->index], + pt->cursor = pCursor); + + } + _dmxSetCursor(screenInfo.screens[pt->index], + pt->cursor, + x + start->rootXOrigin - pt->rootXOrigin, + y + start->rootYOrigin - pt->rootYOrigin); + } + _dmxMoveCursor(screenInfo.screens[pt->index], + x + start->rootXOrigin - pt->rootXOrigin, + y + start->rootYOrigin - pt->rootYOrigin); + } else if (/* pt != start && */ pt->cursorVisible) { + _dmxSetCursor(screenInfo.screens[pt->index], + NULL, + x + start->rootXOrigin - pt->rootXOrigin, + y + start->rootYOrigin - pt->rootYOrigin); + } + if (pt == start) break; + } +} + +static void dmxSetCursor(ScreenPtr pScreen, CursorPtr pCursor, int x, int y) +{ + DMXScreenInfo *start = &dmxScreens[pScreen->myNum]; + DMXScreenInfo *pt; + int GX, GY, gx, gy; + + DMXDBG5("dmxSetCursor(%d %p, %p,%d,%d)\n", + pScreen->myNum, start, pCursor, x, y); + + /* We do this check here because of two cases: + * + * 1) if a client calls XWarpPointer() + * and Xinerama is not running, we can + * have mi's notion of the pointer + * position out of phase with DMX's + * notion. + * + * 2) if a down button is held while the + * cursor moves outside the root window, + * mi's notion of the pointer position + * is out of phase with DMX's notion and + * the cursor can remain visible when it + * shouldn't be. */ + + dmxGetGlobalPosition(&GX, &GY); + gx = start->rootXOrigin + x; + gy = start->rootYOrigin + y; + if (x && y && (GX != gx || GY != gy)) + dmxCoreMotion(gx, gy, 0, DMX_NO_BLOCK); + + if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) { + _dmxSetCursor(pScreen, pCursor, x, y); + return; + } + + for (pt = start->over; /* condition at end of loop */; pt = pt->over) { + if (pt->cursorNotShared) continue; + if (dmxOnScreen(x + start->rootXOrigin, y + start->rootYOrigin, pt)) { + _dmxSetCursor(screenInfo.screens[pt->index], pCursor, + x + start->rootXOrigin - pt->rootXOrigin, + y + start->rootYOrigin - pt->rootYOrigin); + } else { + _dmxSetCursor(screenInfo.screens[pt->index], NULL, + x + start->rootXOrigin - pt->rootXOrigin, + y + start->rootYOrigin - pt->rootYOrigin); + } + if (pt == start) break; + } +} + + +/** This routine is used by the backend input routines to hide the + * cursor on a screen that is being used for relative input. \see + * dmxbackend.c */ +void dmxHideCursor(DMXScreenInfo *dmxScreen) +{ + int x, y; + ScreenPtr pScreen = screenInfo.screens[dmxScreen->index]; + + dmxGetGlobalPosition(&x, &y); + _dmxSetCursor(pScreen, NULL, x, y); +} + +/** This routine is called during reconfiguration to make sure the + * cursor is visible. */ +void dmxCheckCursor(void) +{ + int i; + int x, y; + ScreenPtr pScreen; + DMXScreenInfo *firstScreen; + + dmxGetGlobalPosition(&x, &y); + firstScreen = dmxFindFirstScreen(x, y); + + DMXDBG2("dmxCheckCursor %d %d\n", x, y); + for (i = 0; i < dmxNumScreens; i++) { + DMXScreenInfo *dmxScreen = &dmxScreens[i]; + pScreen = screenInfo.screens[dmxScreen->index]; + + if (!dmxOnScreen(x, y, dmxScreen)) { + if (firstScreen && i == miPointerCurrentScreen()->myNum) + miPointerSetNewScreen(firstScreen->index, x, y); + _dmxSetCursor(pScreen, NULL, + x - dmxScreen->rootXOrigin, + y - dmxScreen->rootYOrigin); + } else { + if (!dmxScreen->cursor) { + CursorPtr pCursor; + + if ((pCursor = dmxFindCursor(dmxScreen))) { + _dmxRealizeCursor(pScreen, dmxScreen->cursor = pCursor); + } + } + _dmxSetCursor(pScreen, dmxScreen->cursor, + x - dmxScreen->rootXOrigin, + y - dmxScreen->rootYOrigin); + } + } + DMXDBG2(" leave dmxCheckCursor %d %d\n", x, y); +} + +miPointerSpriteFuncRec dmxPointerSpriteFuncs = +{ + dmxRealizeCursor, + dmxUnrealizeCursor, + dmxSetCursor, + dmxMoveCursor, +}; |