xserver-multidpi/hw/dmx/dmxcursor.c

1024 lines
32 KiB
C

/*
* 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.
*/
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif
#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() */
#include "inputstr.h" /* for inputInfo.pointer */
#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(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
{
DMXDBG3("dmxWarpCursor(%d,%d,%d)\n", pScreen->myNum, x, y);
#if 11 /*BP*/
/* This call is deprecated. Replace with???? */
miPointerWarpCursor(pDev, pScreen, x, y);
#else
pScreen->SetCursorPosition(pDev, pScreen, x, y, FALSE);
#endif
}
miPointerScreenFuncRec dmxPointerCursorFuncs = {
dmxCursorOffScreen,
dmxCrossScreen,
dmxWarpCursor,
};
/** Create a list of screens that we'll manipulate. */
static int *
dmxSLCreate(void)
{
int *list = xallocarray(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, refScreen;
DMXScreenInfo *screen;
int i, ref;
int changed = 0;
for (i = 0; i < dmxNumScreens; i++) {
if (!screensLeft[i])
continue;
screen = &dmxScreens[i];
pScreen = screenInfo.screens[i];
switch (screen->where) {
case PosAbsolute:
pScreen->x = screen->whereX;
pScreen->y = screen->whereY;
++changed, screensLeft[i] = 0;
break;
case PosRelative:
ref = screen->whereRefScreen;
if (screensLeft[ref])
break;
refScreen = screenInfo.screens[ref];
pScreen->x = refScreen->x + screen->whereX;
pScreen->y = refScreen->y + screen->whereY;
++changed, screensLeft[i] = 0;
break;
case PosRightOf:
ref = screen->whereRefScreen;
if (screensLeft[ref])
break;
refScreen = screenInfo.screens[ref];
pScreen->x = refScreen->x + refScreen->width;
pScreen->y = refScreen->y;
++changed, screensLeft[i] = 0;
break;
case PosLeftOf:
ref = screen->whereRefScreen;
if (screensLeft[ref])
break;
refScreen = screenInfo.screens[ref];
pScreen->x = refScreen->x - pScreen->width;
pScreen->y = refScreen->y;
++changed, screensLeft[i] = 0;
break;
case PosBelow:
ref = screen->whereRefScreen;
if (screensLeft[ref])
break;
refScreen = screenInfo.screens[ref];
pScreen->x = refScreen->x;
pScreen->y = refScreen->y + refScreen->height;
++changed, screensLeft[i] = 0;
break;
case PosAbove:
ref = screen->whereRefScreen;
if (screensLeft[ref])
break;
refScreen = screenInfo.screens[ref];
pScreen->x = refScreen->x;
pScreen->y = refScreen->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)
{
ScreenPtr pScreen;
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;
pScreen = screenInfo.screens[ref];
pScreen->x = pScreen->y = 0;
screensLeft[ref] = 0;
}
}
dmxSLFree(screensLeft);
/* Justify the topmost and leftmost to
* (0,0). */
minX = screenInfo.screens[0]->x;
minY = screenInfo.screens[0]->y;
for (i = 1; i < dmxNumScreens; i++) { /* Compute minX, minY */
if (screenInfo.screens[i]->x < minX)
minX = screenInfo.screens[i]->x;
if (screenInfo.screens[i]->y < minY)
minY = screenInfo.screens[i]->y;
}
if (minX || minY) {
for (i = 0; i < dmxNumScreens; i++) {
screenInfo.screens[i]->x -= minX;
screenInfo.screens[i]->y -= minY;
}
}
update_desktop_dimensions();
}
/** Recompute origin information in the #dmxScreens list. This is
* called from #dmxInitOrigins. */
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 = screenInfo.screens[i]->x;
dmxScreen->rootYOrigin = screenInfo.screens[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 \a 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);
DMX_SET_CURSOR_PRIV(pCursor, pScreen, malloc(sizeof(*pCursorPriv)));
if (!DMX_GET_CURSOR_PRIV(pCursor, pScreen))
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];
DMXDBG2("_dmxUnrealizeCursor(%d,%p)\n", pScreen->myNum, pCursor);
if (dmxScreen->beDisplay) {
if (dmxBEFreeCursor(pScreen, pCursor))
free(DMX_GET_CURSOR_PRIV(pCursor, pScreen));
}
DMX_SET_CURSOR_PRIV(pCursor, pScreen, 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 (pCursorPriv && 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(DeviceIntPtr pDev, 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(DeviceIntPtr pDev, 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(inputInfo.pointer);
for (pt = start->over; /* condition at end of loop */ ; pt = pt->over) {
if (pt->cursor)
return pt->cursor;
if (pt == start)
break;
}
return GetSpriteCursor(inputInfo.pointer);
}
/** 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.
*
* The coords (x,y) are in global coord space. We'll loop over the
* back-end screens and see if they contain the global coord. If so, call
* _dmxMoveCursor() (XWarpPointer) to position the pointer on that screen.
*/
void
dmxMoveCursor(DeviceIntPtr pDev, 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(DeviceIntPtr pDev, 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(NULL, 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 == miPointerGetScreen(inputInfo.pointer)->myNum)
miPointerSetScreen(inputInfo.pointer, 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);
}
static Bool
dmxDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr)
{
return TRUE;
}
static void
dmxDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr)
{
}
miPointerSpriteFuncRec dmxPointerSpriteFuncs = {
dmxRealizeCursor,
dmxUnrealizeCursor,
dmxSetCursor,
dmxMoveCursor,
dmxDeviceCursorInitialize,
dmxDeviceCursorCleanup
};