dmxcursor.c revision 706f2543
1/*
2 * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 *   David H. Dawes <dawes@xfree86.org>
31 *   Kevin E. Martin <kem@redhat.com>
32 *   Rickard E. (Rik) Faith <faith@redhat.com>
33 *
34 */
35
36/** \file
37 * This file contains code than supports cursor movement, including the
38 * code that initializes and reinitializes the screen positions and
39 * computes screen overlap.
40 *
41 * "This code is based very closely on the XFree86 equivalent
42 * (xfree86/common/xf86Cursor.c)."  --David Dawes.
43 *
44 * "This code was then extensively re-written, as explained here."
45 * --Rik Faith
46 *
47 * The code in xf86Cursor.c used edge lists to implement the
48 * CursorOffScreen function.  The edge list computation was complex
49 * (especially in the face of arbitrarily overlapping screens) compared
50 * with the speed savings in the CursorOffScreen function.  The new
51 * implementation has erred on the side of correctness, readability, and
52 * maintainability over efficiency.  For the common (non-edge) case, the
53 * dmxCursorOffScreen function does avoid a loop over all the screens.
54 * When the cursor has left the screen, all the screens are searched,
55 * and the first screen (in dmxScreens order) containing the cursor will
56 * be returned.  If run-time profiling shows that this routing is a
57 * performance bottle-neck, then an edge list may have to be
58 * reimplemented.  An edge list algorithm is O(edges) whereas the new
59 * algorithm is O(dmxNumScreens).  Since edges is usually 1-3 and
60 * dmxNumScreens may be 30-60 for large backend walls, this trade off
61 * may be compelling.
62 *
63 * The xf86InitOrigins routine uses bit masks during the computation and
64 * is therefore limited to the length of a word (e.g., 32 or 64 bits)
65 * screens.  Because Xdmx is expected to be used with a large number of
66 * backend displays, this limitation was removed.  The new
67 * implementation has erred on the side of readability over efficiency,
68 * using the dmxSL* routines to manage a screen list instead of a
69 * bitmap, and a function call to decrease the length of the main
70 * routine.  Both algorithms are of the same order, and both are called
71 * only at server generation time, so trading clarity and long-term
72 * maintainability for efficiency does not seem justified in this case.
73 */
74
75#ifdef HAVE_DMX_CONFIG_H
76#include <dmx-config.h>
77#endif
78
79#define DMX_CURSOR_DEBUG 0
80
81#include "dmx.h"
82#include "dmxsync.h"
83#include "dmxcursor.h"
84#include "dmxlog.h"
85#include "dmxprop.h"
86#include "dmxinput.h"
87
88#include "mipointer.h"
89#include "windowstr.h"
90#include "globals.h"
91#include "cursorstr.h"
92#include "dixevents.h"          /* For GetSpriteCursor() */
93#include "inputstr.h"           /* for inputInfo.pointer */
94
95#if DMX_CURSOR_DEBUG
96#define DMXDBG0(f)               dmxLog(dmxDebug,f)
97#define DMXDBG1(f,a)             dmxLog(dmxDebug,f,a)
98#define DMXDBG2(f,a,b)           dmxLog(dmxDebug,f,a,b)
99#define DMXDBG3(f,a,b,c)         dmxLog(dmxDebug,f,a,b,c)
100#define DMXDBG4(f,a,b,c,d)       dmxLog(dmxDebug,f,a,b,c,d)
101#define DMXDBG5(f,a,b,c,d,e)     dmxLog(dmxDebug,f,a,b,c,d,e)
102#define DMXDBG6(f,a,b,c,d,e,g)   dmxLog(dmxDebug,f,a,b,c,d,e,g)
103#define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
104#else
105#define DMXDBG0(f)
106#define DMXDBG1(f,a)
107#define DMXDBG2(f,a,b)
108#define DMXDBG3(f,a,b,c)
109#define DMXDBG4(f,a,b,c,d)
110#define DMXDBG5(f,a,b,c,d,e)
111#define DMXDBG6(f,a,b,c,d,e,g)
112#define DMXDBG7(f,a,b,c,d,e,g,h)
113#endif
114
115static int dmxCursorDoMultiCursors = 1;
116
117/** Turn off support for displaying multiple cursors on overlapped
118    back-end displays.  See #dmxCursorDoMultiCursors. */
119void dmxCursorNoMulti(void)
120{
121    dmxCursorDoMultiCursors = 0;
122}
123
124static Bool dmxCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
125{
126    DMXScreenInfo *dmxScreen;
127    int           i;
128    int           localX = *x;
129    int           localY = *y;
130    int           globalX;
131    int           globalY;
132
133    if (screenInfo.numScreens == 1)
134        return FALSE;
135
136                                /* On current screen? */
137    dmxScreen = &dmxScreens[(*ppScreen)->myNum];
138    if (localX >= 0
139        && localX < dmxScreen->rootWidth
140        && localY >= 0
141        && localY < dmxScreen->rootHeight)
142        return FALSE;
143
144                                /* Convert to global coordinate space */
145    globalX = dmxScreen->rootXOrigin + localX;
146    globalY = dmxScreen->rootYOrigin + localY;
147
148                                /* Is cursor on the current screen?
149                                 * This efficiently exits this routine
150                                 * for the most common case. */
151    if (ppScreen && *ppScreen) {
152        dmxScreen = &dmxScreens[(*ppScreen)->myNum];
153        if (globalX >= dmxScreen->rootXOrigin
154            && globalX < dmxScreen->rootXOrigin + dmxScreen->rootWidth
155            && globalY >= dmxScreen->rootYOrigin
156            && globalY < dmxScreen->rootYOrigin + dmxScreen->rootHeight)
157            return FALSE;
158    }
159
160                                /* Find first screen cursor is on */
161    for (i = 0; i < dmxNumScreens; i++) {
162        dmxScreen = &dmxScreens[i];
163        if (globalX >= dmxScreen->rootXOrigin
164            && globalX < dmxScreen->rootXOrigin + dmxScreen->rootWidth
165            && globalY >= dmxScreen->rootYOrigin
166            && globalY < dmxScreen->rootYOrigin + dmxScreen->rootHeight) {
167            if (dmxScreen->index == (*ppScreen)->myNum)
168                return FALSE;
169            *ppScreen = screenInfo.screens[dmxScreen->index];
170            *x        = globalX - dmxScreen->rootXOrigin;
171            *y        = globalY - dmxScreen->rootYOrigin;
172            return TRUE;
173        }
174    }
175    return FALSE;
176}
177
178static void dmxCrossScreen(ScreenPtr pScreen, Bool entering)
179{
180}
181
182static void dmxWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
183{
184    DMXDBG3("dmxWarpCursor(%d,%d,%d)\n", pScreen->myNum, x, y);
185#if 11 /*BP*/
186    /* This call is depracated.  Replace with???? */
187    miPointerWarpCursor(pDev, pScreen, x, y);
188#else
189    pScreen->SetCursorPosition(pDev, pScreen, x, y, FALSE);
190#endif
191}
192
193miPointerScreenFuncRec dmxPointerCursorFuncs =
194{
195    dmxCursorOffScreen,
196    dmxCrossScreen,
197    dmxWarpCursor,
198    NULL,
199    NULL,
200};
201
202
203/** Create a list of screens that we'll manipulate. */
204static int *dmxSLCreate(void)
205{
206    int *list = malloc(dmxNumScreens * sizeof(*list));
207    int i;
208
209    for (i = 0; i < dmxNumScreens; i++)
210        list[i] = 1;
211    return list;
212}
213
214/** Free list. */
215static void dmxSLFree(int *list)
216{
217    free(list);
218}
219
220/** Find next uninitialized entry in list. */
221static int dmxSLFindNext(int *list)
222{
223    int i;
224    for (i = 0; i < dmxNumScreens; i++)
225        if (list[i])
226            return i;
227    return -1;
228}
229
230/** Make one pass over all the screens and return the number updated. */
231static int dmxTryComputeScreenOrigins(int *screensLeft)
232{
233    ScreenPtr       pScreen, refScreen;
234    DMXScreenInfo   *screen;
235    int             i, ref;
236    int             changed = 0;
237
238    for (i = 0; i < dmxNumScreens; i++) {
239        if (!screensLeft[i])
240            continue;
241        screen  = &dmxScreens[i];
242        pScreen = screenInfo.screens[i];
243        switch (screen->where) {
244        case PosAbsolute:
245            pScreen->x = screen->whereX;
246            pScreen->y = screen->whereY;
247            ++changed, screensLeft[i] = 0;
248            break;
249        case PosRelative:
250            ref = screen->whereRefScreen;
251            if (screensLeft[ref])
252                break;
253            refScreen = screenInfo.screens[ref];
254            pScreen->x = refScreen->x + screen->whereX;
255            pScreen->y = refScreen->y + screen->whereY;
256            ++changed, screensLeft[i] = 0;
257            break;
258        case PosRightOf:
259            ref = screen->whereRefScreen;
260            if (screensLeft[ref])
261                break;
262            refScreen = screenInfo.screens[ref];
263            pScreen->x = refScreen->x + refScreen->width;
264            pScreen->y = refScreen->y;
265            ++changed, screensLeft[i] = 0;
266            break;
267        case PosLeftOf:
268            ref = screen->whereRefScreen;
269            if (screensLeft[ref])
270                break;
271            refScreen = screenInfo.screens[ref];
272            pScreen->x = refScreen->x - pScreen->width;
273            pScreen->y = refScreen->y;
274            ++changed, screensLeft[i] = 0;
275            break;
276        case PosBelow:
277            ref = screen->whereRefScreen;
278            if (screensLeft[ref])
279                break;
280            refScreen = screenInfo.screens[ref];
281            pScreen->x = refScreen->x;
282            pScreen->y = refScreen->y + refScreen->height;
283            ++changed, screensLeft[i] = 0;
284            break;
285        case PosAbove:
286            ref = screen->whereRefScreen;
287            if (screensLeft[ref])
288                break;
289            refScreen = screenInfo.screens[ref];
290            pScreen->x = refScreen->x;
291            pScreen->y = refScreen->y - pScreen->height;
292            ++changed, screensLeft[i] = 0;
293            break;
294        case PosNone:
295            dmxLog(dmxFatal, "No position information for screen %d\n", i);
296        }
297    }
298    return changed;
299}
300
301static void dmxComputeScreenOrigins(void)
302{
303    ScreenPtr       pScreen;
304    int             *screensLeft;
305    int             i, ref;
306    int             minX, minY;
307
308                                /* Compute origins based on
309                                 * configuration information. */
310    screensLeft = dmxSLCreate();
311    while ((i = dmxSLFindNext(screensLeft)) >= 0) {
312        while (dmxTryComputeScreenOrigins(screensLeft));
313        if ((i = dmxSLFindNext(screensLeft)) >= 0) {
314	    /* All of the remaining screens are referencing each other.
315	     * Assign a value to one of them and go through again.  This
316	     * guarantees that we will eventually terminate.
317	     */
318	    ref                     = dmxScreens[i].whereRefScreen;
319	    pScreen                 = screenInfo.screens[ref];
320	    pScreen->x = pScreen->y = 0;
321	    screensLeft[ref]        = 0;
322	}
323    }
324    dmxSLFree(screensLeft);
325
326
327                                /* Justify the topmost and leftmost to
328                                 * (0,0). */
329    minX = screenInfo.screens[0]->x;
330    minY = screenInfo.screens[0]->y;
331    for (i = 1; i < dmxNumScreens; i++) { /* Compute minX, minY */
332	if (screenInfo.screens[i]->x < minX)
333            minX = screenInfo.screens[i]->x;
334	if (screenInfo.screens[i]->y < minY)
335            minY = screenInfo.screens[i]->y;
336    }
337    if (minX || minY) {
338	for (i = 0; i < dmxNumScreens; i++) {
339	    screenInfo.screens[i]->x -= minX;
340	    screenInfo.screens[i]->y -= minY;
341	}
342    }
343}
344
345/** Recompute origin information in the #dmxScreens list.  This is
346 * called from #dmxInitOrigins. */
347void dmxReInitOrigins(void)
348{
349    int        i;
350
351    if (dmxNumScreens > MAXSCREENS)
352        dmxLog(dmxFatal, "dmxNumScreens = %d > MAXSCREENS = %d\n",
353               dmxNumScreens, MAXSCREENS);
354
355    for (i = 0; i < dmxNumScreens; i++) {
356        DMXScreenInfo    *dmxScreen  = &dmxScreens[i];
357        dmxLogOutput(dmxScreen,
358                     "s=%dx%d%+d%+d r=%dx%d%+d%+d @%d,%d"
359                     " (be=%dx%d depth=%d bpp=%d)\n",
360                     dmxScreen->scrnWidth, dmxScreen->scrnHeight,
361                     dmxScreen->scrnX, dmxScreen->scrnY,
362
363                     dmxScreen->rootWidth, dmxScreen->rootHeight,
364                     dmxScreen->rootX, dmxScreen->rootY,
365
366                     dmxScreen->rootXOrigin, dmxScreen->rootYOrigin,
367                     dmxScreen->beWidth, dmxScreen->beHeight,
368                     dmxScreen->beDepth, dmxScreen->beBPP);
369    }
370}
371
372/** Initialize screen origins (and relative position).  This is called
373 * for each server generation.  For dynamic reconfiguration, use
374 * #dmxReInitOrigins() instead. */
375void dmxInitOrigins(void)
376{
377    int  i;
378
379    if (dmxNumScreens > MAXSCREENS)
380        dmxLog(dmxFatal, "dmxNumScreens = %d > MAXSCREENS = %d\n",
381               dmxNumScreens, MAXSCREENS);
382
383    for (i = 0; i < dmxNumScreens; i++) {
384        DMXScreenInfo    *dmxScreen  = &dmxScreens[i];
385        dmxLogOutput(dmxScreen,
386                     "(request) s=%dx%d%+d%+d r=%dx%d%+d%+d @%d,%d (%d)"
387                     " (be=%dx%d depth=%d bpp=%d)\n",
388                     dmxScreen->scrnWidth, dmxScreen->scrnHeight,
389                     dmxScreen->scrnX, dmxScreen->scrnY,
390
391                     dmxScreen->rootWidth, dmxScreen->rootHeight,
392                     dmxScreen->rootX, dmxScreen->rootY,
393
394                     dmxScreen->whereX, dmxScreen->whereY,
395                     dmxScreen->where,
396
397                     dmxScreen->beWidth, dmxScreen->beHeight,
398                     dmxScreen->beDepth, dmxScreen->beBPP);
399    }
400
401    dmxComputeScreenOrigins();
402
403    for (i = 0; i < dmxNumScreens; i++) {
404        DMXScreenInfo  *dmxScreen = &dmxScreens[i];
405        dmxScreen->rootXOrigin = screenInfo.screens[i]->x;
406        dmxScreen->rootYOrigin = screenInfo.screens[i]->y;
407    }
408
409    dmxReInitOrigins();
410}
411
412/** Returns non-zero if the global \a x, \a y coordinate is on the
413 * screen window of the \a dmxScreen. */
414int dmxOnScreen(int x, int y, DMXScreenInfo *dmxScreen)
415{
416#if DMX_CURSOR_DEBUG > 1
417    dmxLog(dmxDebug,
418           "dmxOnScreen %d %d,%d (r=%dx%d%+d%+d@%d,%d s=%dx%d%+d%+d)\n",
419           dmxScreen->index, x, y,
420           dmxScreen->rootWidth, dmxScreen->rootHeight,
421           dmxScreen->rootX, dmxScreen->rootY,
422           dmxScreen->rootXOrigin, dmxScreen->rootYOrigin,
423           dmxScreen->scrnWidth, dmxScreen->scrnHeight,
424           dmxScreen->scrnX, dmxScreen->scrnY);
425#endif
426    if (x >= dmxScreen->rootXOrigin
427        && x < dmxScreen->rootXOrigin + dmxScreen->rootWidth
428        && y >= dmxScreen->rootYOrigin
429        && y < dmxScreen->rootYOrigin + dmxScreen->rootHeight) return 1;
430    return 0;
431}
432
433/** Returns non-zero if \a a overlaps \a b. */
434static int dmxDoesOverlap(DMXScreenInfo *a, DMXScreenInfo *b)
435{
436    if (dmxOnScreen(a->rootXOrigin,
437                    a->rootYOrigin,                 b))
438        return 1;
439
440    if (dmxOnScreen(a->rootXOrigin,
441                    a->rootYOrigin + a->scrnWidth,  b))
442        return 1;
443
444    if (dmxOnScreen(a->rootXOrigin + a->scrnHeight,
445                    a->rootYOrigin,                 b))
446        return 1;
447
448    if (dmxOnScreen(a->rootXOrigin + a->scrnHeight,
449                    a->rootYOrigin + a->scrnWidth,  b))
450        return 1;
451
452    if (dmxOnScreen(b->rootXOrigin,
453                    b->rootYOrigin,                 a))
454        return 1;
455
456    if (dmxOnScreen(b->rootXOrigin,
457                    b->rootYOrigin + b->scrnWidth,  a))
458        return 1;
459
460    if (dmxOnScreen(b->rootXOrigin + b->scrnHeight,
461                    b->rootYOrigin,                 a))
462        return 1;
463
464    if (dmxOnScreen(b->rootXOrigin + b->scrnHeight,
465                    b->rootYOrigin + b->scrnWidth,  a))
466        return 1;
467
468    return 0;
469}
470
471/** Used with \a dmxInterateOverlap to print out a list of screens which
472 * overlap each other. */
473static void *dmxPrintOverlap(DMXScreenInfo *dmxScreen, void *closure)
474{
475    DMXScreenInfo *a = closure;
476    if (dmxScreen != a) {
477        if (dmxScreen->cursorNotShared)
478            dmxLogOutputCont(a, " [%d/%s]", dmxScreen->index, dmxScreen->name);
479        else
480            dmxLogOutputCont(a, " %d/%s", dmxScreen->index, dmxScreen->name);
481    }
482    return NULL;
483}
484
485/** Iterate over the screens which overlap with the \a start screen,
486 * calling \a f with the \a closure for each argument.  Often used with
487 * #dmxPrintOverlap. */
488static void *dmxIterateOverlap(DMXScreenInfo *start,
489                               void *(*f)(DMXScreenInfo *dmxScreen, void *),
490                               void *closure)
491{
492    DMXScreenInfo *pt;
493
494    if (!start->over) return f(start, closure);
495
496    for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
497        void *retval;
498        if ((retval = f(pt, closure))) return retval;
499        if (pt == start) break;
500    }
501    return NULL;
502}
503
504/** Used with #dmxPropertyIterate to determine if screen \a a is the
505 * same as the screen \a closure. */
506static void *dmxTestSameDisplay(DMXScreenInfo *a, void *closure)
507{
508    DMXScreenInfo *b = closure;
509
510    if (a == b)
511        return a;
512    return NULL;
513}
514
515/** Detects overlapping dmxScreens and creates circular lists.  This
516 * uses an O(dmxNumScreens^2) algorithm, but dmxNumScreens is < 100 and
517 * the computation only needs to be performed for every server
518 * generation or dynamic reconfiguration . */
519void dmxInitOverlap(void)
520{
521    int           i, j;
522    DMXScreenInfo *a, *b, *pt;
523
524    for (i = 0; i < dmxNumScreens; i++)
525        dmxScreens[i].over = NULL;
526
527    for (i = 0; i < dmxNumScreens; i++) {
528        a = &dmxScreens[i];
529
530        for (j = i+1; j < dmxNumScreens; j++) {
531            b = &dmxScreens[j];
532            if (b->over)
533                continue;
534
535            if (dmxDoesOverlap(a, b)) {
536                DMXDBG6("%d overlaps %d: a=%p %p b=%p %p\n",
537                        a->index, b->index, a, a->over, b, b->over);
538                b->over = (a->over ? a->over : a);
539                a->over = b;
540            }
541        }
542    }
543
544    for (i = 0; i < dmxNumScreens; i++) {
545        a = &dmxScreens[i];
546
547        if (!a->over)
548            continue;
549
550                                /* Flag all pairs that are on same display */
551        for (pt = a->over; pt != a; pt = pt->over) {
552            if (dmxPropertyIterate(a, dmxTestSameDisplay, pt)) {
553                /* The ->over sets contain the transitive set of screens
554                 * that overlap.  For screens that are on the same
555                 * backend display, we only want to exclude pairs of
556                 * screens that mutually overlap on the backend display,
557                 * so we call dmxDoesOverlap, which is stricter than the
558                 * ->over set. */
559                if (!dmxDoesOverlap(a, pt))
560                    continue;
561                a->cursorNotShared  = 1;
562                pt->cursorNotShared = 1;
563                dmxLog(dmxInfo,
564                       "Screen %d and %d overlap on %s\n",
565                       a->index, pt->index, a->name);
566            }
567        }
568    }
569
570    for (i = 0; i < dmxNumScreens; i++) {
571        a = &dmxScreens[i];
572
573        if (a->over) {
574            dmxLogOutput(a, "Overlaps");
575            dmxIterateOverlap(a, dmxPrintOverlap, a);
576            dmxLogOutputCont(a, "\n");
577        }
578    }
579}
580
581/** Create \a pCursor on the back-end associated with \a pScreen. */
582void dmxBECreateCursor(ScreenPtr pScreen, CursorPtr pCursor)
583{
584    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
585    dmxCursorPrivPtr  pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen);
586    CursorBitsPtr     pBits = pCursor->bits;
587    Pixmap            src, msk;
588    XColor            fg, bg;
589    XImage           *img;
590    XlibGC            gc = NULL;
591    XGCValues         v;
592    unsigned long     m;
593    int               i;
594
595    if (!pCursorPriv)
596	return;
597
598    m = GCFunction | GCPlaneMask | GCForeground | GCBackground | GCClipMask;
599    v.function = GXcopy;
600    v.plane_mask = AllPlanes;
601    v.foreground = 1L;
602    v.background = 0L;
603    v.clip_mask = None;
604
605    for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) {
606	if (dmxScreen->bePixmapFormats[i].depth == 1) {
607	    /* Create GC in the back-end servers */
608	    gc = XCreateGC(dmxScreen->beDisplay, dmxScreen->scrnDefDrawables[i],
609			   m, &v);
610	    break;
611	}
612    }
613    if (!gc)
614        dmxLog(dmxFatal, "dmxRealizeCursor: gc not initialized\n");
615
616    src = XCreatePixmap(dmxScreen->beDisplay, dmxScreen->scrnWin,
617			pBits->width, pBits->height, 1);
618    msk = XCreatePixmap(dmxScreen->beDisplay, dmxScreen->scrnWin,
619			pBits->width, pBits->height, 1);
620
621    img = XCreateImage(dmxScreen->beDisplay,
622		       dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual,
623		       1, XYBitmap, 0, (char *)pBits->source,
624		       pBits->width, pBits->height,
625		       BitmapPad(dmxScreen->beDisplay), 0);
626
627    XPutImage(dmxScreen->beDisplay, src, gc, img, 0, 0, 0, 0,
628	      pBits->width, pBits->height);
629
630    XFree(img);
631
632    img = XCreateImage(dmxScreen->beDisplay,
633		       dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual,
634		       1, XYBitmap, 0, (char *)pBits->mask,
635		       pBits->width, pBits->height,
636		       BitmapPad(dmxScreen->beDisplay), 0);
637
638    XPutImage(dmxScreen->beDisplay, msk, gc, img, 0, 0, 0, 0,
639	      pBits->width, pBits->height);
640
641    XFree(img);
642
643    fg.red   = pCursor->foreRed;
644    fg.green = pCursor->foreGreen;
645    fg.blue  = pCursor->foreBlue;
646
647    bg.red   = pCursor->backRed;
648    bg.green = pCursor->backGreen;
649    bg.blue  = pCursor->backBlue;
650
651    pCursorPriv->cursor = XCreatePixmapCursor(dmxScreen->beDisplay,
652					      src, msk,
653					      &fg, &bg,
654					      pBits->xhot, pBits->yhot);
655
656    XFreePixmap(dmxScreen->beDisplay, src);
657    XFreePixmap(dmxScreen->beDisplay, msk);
658    XFreeGC(dmxScreen->beDisplay, gc);
659
660    dmxSync(dmxScreen, FALSE);
661}
662
663static Bool _dmxRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor)
664{
665    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
666    dmxCursorPrivPtr  pCursorPriv;
667
668    DMXDBG2("_dmxRealizeCursor(%d,%p)\n", pScreen->myNum, pCursor);
669
670    DMX_SET_CURSOR_PRIV(pCursor, pScreen, malloc(sizeof(*pCursorPriv)));
671    if (!DMX_GET_CURSOR_PRIV(pCursor, pScreen))
672	return FALSE;
673
674    pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen);
675    pCursorPriv->cursor = (Cursor)0;
676
677    if (!dmxScreen->beDisplay)
678	return TRUE;
679
680    dmxBECreateCursor(pScreen, pCursor);
681    return TRUE;
682}
683
684/** Free \a pCursor on the back-end associated with \a pScreen. */
685Bool dmxBEFreeCursor(ScreenPtr pScreen, CursorPtr pCursor)
686{
687    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
688    dmxCursorPrivPtr  pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen);
689
690    if (pCursorPriv) {
691	XFreeCursor(dmxScreen->beDisplay, pCursorPriv->cursor);
692	pCursorPriv->cursor = (Cursor)0;
693	return TRUE;
694    }
695
696    return FALSE;
697}
698
699static Bool _dmxUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor)
700{
701    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
702
703    DMXDBG2("_dmxUnrealizeCursor(%d,%p)\n",
704            pScreen->myNum, pCursor);
705
706    if (dmxScreen->beDisplay) {
707	if (dmxBEFreeCursor(pScreen, pCursor))
708	    free(DMX_GET_CURSOR_PRIV(pCursor, pScreen));
709    }
710    DMX_SET_CURSOR_PRIV(pCursor, pScreen, NULL);
711
712    return TRUE;
713}
714
715static void _dmxMoveCursor(ScreenPtr pScreen, int x, int y)
716{
717    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
718    int           newX       = x + dmxScreen->rootX;
719    int           newY       = y + dmxScreen->rootY;
720
721    if (newX < 0) newX = 0;
722    if (newY < 0) newY = 0;
723
724    DMXDBG5("_dmxMoveCursor(%d,%d,%d) -> %d,%d\n",
725            pScreen->myNum, x, y, newX, newY);
726    if (dmxScreen->beDisplay) {
727	XWarpPointer(dmxScreen->beDisplay, None, dmxScreen->scrnWin,
728		     0, 0, 0, 0, newX, newY);
729	dmxSync(dmxScreen, TRUE);
730    }
731}
732
733static void _dmxSetCursor(ScreenPtr pScreen, CursorPtr pCursor, int x, int y)
734{
735    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
736
737    DMXDBG4("_dmxSetCursor(%d,%p,%d,%d)\n", pScreen->myNum, pCursor, x, y);
738
739    if (pCursor) {
740	dmxCursorPrivPtr  pCursorPriv = DMX_GET_CURSOR_PRIV(pCursor, pScreen);
741	if (pCursorPriv && dmxScreen->curCursor != pCursorPriv->cursor) {
742	    if (dmxScreen->beDisplay)
743		XDefineCursor(dmxScreen->beDisplay, dmxScreen->scrnWin,
744			      pCursorPriv->cursor);
745            dmxScreen->cursor        = pCursor;
746	    dmxScreen->curCursor     = pCursorPriv->cursor;
747            dmxScreen->cursorVisible = 1;
748	}
749	_dmxMoveCursor(pScreen, x, y);
750    } else {
751	if (dmxScreen->beDisplay)
752	    XDefineCursor(dmxScreen->beDisplay, dmxScreen->scrnWin,
753			  dmxScreen->noCursor);
754        dmxScreen->cursor        = NULL;
755	dmxScreen->curCursor     = (Cursor)0;
756        dmxScreen->cursorVisible = 0;
757    }
758    if (dmxScreen->beDisplay) dmxSync(dmxScreen, TRUE);
759}
760
761static Bool dmxRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
762{
763    DMXScreenInfo *start = &dmxScreens[pScreen->myNum];
764    DMXScreenInfo *pt;
765
766    if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared)
767        return _dmxRealizeCursor(pScreen, pCursor);
768
769    for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
770        if (pt->cursorNotShared)
771            continue;
772        _dmxRealizeCursor(screenInfo.screens[pt->index], pCursor);
773        if (pt == start)
774            break;
775    }
776    return TRUE;
777}
778
779static Bool dmxUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
780{
781    DMXScreenInfo *start = &dmxScreens[pScreen->myNum];
782    DMXScreenInfo *pt;
783
784    if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared)
785        return _dmxUnrealizeCursor(pScreen, pCursor);
786
787    for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
788        if (pt->cursorNotShared)
789            continue;
790        _dmxUnrealizeCursor(screenInfo.screens[pt->index], pCursor);
791        if (pt == start)
792            break;
793    }
794    return TRUE;
795}
796
797static CursorPtr dmxFindCursor(DMXScreenInfo *start)
798{
799    DMXScreenInfo *pt;
800
801    if (!start || !start->over)
802        return GetSpriteCursor(inputInfo.pointer);
803    for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
804        if (pt->cursor)
805            return pt->cursor;
806        if (pt == start)
807            break;
808    }
809    return GetSpriteCursor(inputInfo.pointer);
810}
811
812/** Move the cursor to coordinates (\a x, \a y)on \a pScreen.  This
813 * function is usually called via #dmxPointerSpriteFuncs, except during
814 * reconfiguration when the cursor is repositioned to force an update on
815 * newley overlapping screens and on screens that no longer overlap.
816 *
817 * The coords (x,y) are in global coord space.  We'll loop over the
818 * back-end screens and see if they contain the global coord.  If so, call
819 * _dmxMoveCursor() (XWarpPointer) to position the pointer on that screen.
820 */
821void dmxMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
822{
823    DMXScreenInfo *start = &dmxScreens[pScreen->myNum];
824    DMXScreenInfo *pt;
825
826    DMXDBG3("dmxMoveCursor(%d,%d,%d)\n", pScreen->myNum, x, y);
827
828    if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) {
829        _dmxMoveCursor(pScreen, x, y);
830        return;
831    }
832
833    for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
834        if (pt->cursorNotShared)
835            continue;
836        if (dmxOnScreen(x + start->rootXOrigin, y + start->rootYOrigin, pt)) {
837            if (/* pt != start && */ !pt->cursorVisible) {
838                if (!pt->cursor) {
839                                /* This only happens during
840                                 * reconfiguration when a new overlap
841                                 * occurs. */
842                    CursorPtr pCursor;
843
844                    if ((pCursor = dmxFindCursor(start)))
845                        _dmxRealizeCursor(screenInfo.screens[pt->index],
846                                          pt->cursor = pCursor);
847
848                }
849                _dmxSetCursor(screenInfo.screens[pt->index],
850                              pt->cursor,
851                              x + start->rootXOrigin - pt->rootXOrigin,
852                              y + start->rootYOrigin - pt->rootYOrigin);
853            }
854            _dmxMoveCursor(screenInfo.screens[pt->index],
855                           x + start->rootXOrigin - pt->rootXOrigin,
856                           y + start->rootYOrigin - pt->rootYOrigin);
857        } else if (/* pt != start && */ pt->cursorVisible) {
858            _dmxSetCursor(screenInfo.screens[pt->index],
859                          NULL,
860                          x + start->rootXOrigin - pt->rootXOrigin,
861                          y + start->rootYOrigin - pt->rootYOrigin);
862        }
863        if (pt == start)
864            break;
865    }
866}
867
868static void dmxSetCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor, int x, int y)
869{
870    DMXScreenInfo *start = &dmxScreens[pScreen->myNum];
871    DMXScreenInfo *pt;
872    int           GX, GY, gx, gy;
873
874    DMXDBG5("dmxSetCursor(%d %p, %p,%d,%d)\n",
875            pScreen->myNum, start, pCursor, x, y);
876
877                                /* We do this check here because of two cases:
878                                 *
879                                 * 1) if a client calls XWarpPointer()
880                                 * and Xinerama is not running, we can
881                                 * have mi's notion of the pointer
882                                 * position out of phase with DMX's
883                                 * notion.
884                                 *
885                                 * 2) if a down button is held while the
886                                 * cursor moves outside the root window,
887                                 * mi's notion of the pointer position
888                                 * is out of phase with DMX's notion and
889                                 * the cursor can remain visible when it
890                                 * shouldn't be. */
891
892    dmxGetGlobalPosition(&GX, &GY);
893    gx = start->rootXOrigin + x;
894    gy = start->rootYOrigin + y;
895    if (x && y && (GX != gx || GY != gy))
896        dmxCoreMotion(NULL, gx, gy, 0, DMX_NO_BLOCK);
897
898    if (!start->over || !dmxCursorDoMultiCursors || start->cursorNotShared) {
899        _dmxSetCursor(pScreen, pCursor, x, y);
900        return;
901    }
902
903    for (pt = start->over; /* condition at end of loop */; pt = pt->over) {
904        if (pt->cursorNotShared)
905            continue;
906        if (dmxOnScreen(x + start->rootXOrigin, y + start->rootYOrigin, pt)) {
907            _dmxSetCursor(screenInfo.screens[pt->index], pCursor,
908                          x + start->rootXOrigin - pt->rootXOrigin,
909                          y + start->rootYOrigin - pt->rootYOrigin);
910        } else {
911            _dmxSetCursor(screenInfo.screens[pt->index], NULL,
912                          x + start->rootXOrigin - pt->rootXOrigin,
913                          y + start->rootYOrigin - pt->rootYOrigin);
914        }
915        if (pt == start)
916            break;
917    }
918}
919
920
921/** This routine is used by the backend input routines to hide the
922 * cursor on a screen that is being used for relative input.  \see
923 * dmxbackend.c */
924void dmxHideCursor(DMXScreenInfo *dmxScreen)
925{
926    int       x, y;
927    ScreenPtr pScreen = screenInfo.screens[dmxScreen->index];
928
929    dmxGetGlobalPosition(&x, &y);
930    _dmxSetCursor(pScreen, NULL, x, y);
931}
932
933/** This routine is called during reconfiguration to make sure the
934 * cursor is visible. */
935void dmxCheckCursor(void)
936{
937    int           i;
938    int           x, y;
939    ScreenPtr     pScreen;
940    DMXScreenInfo *firstScreen;
941
942    dmxGetGlobalPosition(&x, &y);
943    firstScreen = dmxFindFirstScreen(x, y);
944
945    DMXDBG2("dmxCheckCursor %d %d\n", x, y);
946    for (i = 0; i < dmxNumScreens; i++) {
947        DMXScreenInfo *dmxScreen = &dmxScreens[i];
948        pScreen                  = screenInfo.screens[dmxScreen->index];
949
950        if (!dmxOnScreen(x, y, dmxScreen)) {
951            if (firstScreen && i == miPointerGetScreen(inputInfo.pointer)->myNum)
952                miPointerSetScreen(inputInfo.pointer, firstScreen->index, x, y);
953            _dmxSetCursor(pScreen, NULL,
954                          x - dmxScreen->rootXOrigin,
955                          y - dmxScreen->rootYOrigin);
956        } else {
957            if (!dmxScreen->cursor) {
958                CursorPtr pCursor;
959
960                if ((pCursor = dmxFindCursor(dmxScreen))) {
961                    _dmxRealizeCursor(pScreen, dmxScreen->cursor = pCursor);
962                }
963            }
964            _dmxSetCursor(pScreen, dmxScreen->cursor,
965                          x - dmxScreen->rootXOrigin,
966                          y - dmxScreen->rootYOrigin);
967        }
968    }
969    DMXDBG2("   leave dmxCheckCursor %d %d\n", x, y);
970}
971
972static Bool dmxDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr)
973{
974    return TRUE;
975}
976
977static void dmxDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr)
978{
979}
980
981miPointerSpriteFuncRec dmxPointerSpriteFuncs =
982{
983    dmxRealizeCursor,
984    dmxUnrealizeCursor,
985    dmxSetCursor,
986    dmxMoveCursor,
987    dmxDeviceCursorInitialize,
988    dmxDeviceCursorCleanup
989};
990