979 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			979 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() */
 | |
| 
 | |
| #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);
 | |
| #if 11 /*BP*/
 | |
|     /* This call is depracated.  Replace with???? */
 | |
|     miPointerWarpCursor(pScreen, x, y);
 | |
| #else
 | |
|     pScreen->SetCursorPosition(pScreen, x, y, FALSE);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| miPointerScreenFuncRec dmxPointerCursorFuncs =
 | |
| {
 | |
|     dmxCursorOffScreen,
 | |
|     dmxCrossScreen,
 | |
|     dmxWarpCursor,
 | |
|     dmxeqEnqueue,        /*XXX incompatible type/function! */
 | |
|     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];
 | |
| 
 | |
|     DMXDBG2("_dmxUnrealizeCursor(%d,%p)\n",
 | |
|             pScreen->myNum, pCursor);
 | |
| 
 | |
|     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.
 | |
|  *
 | |
|  * 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(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(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 00
 | |
|             if (firstScreen && i == miPointerCurrentScreen()->myNum)
 | |
|                 miPointerSetNewScreen(firstScreen->index, x, y);
 | |
| #else
 | |
|             if (firstScreen && i == miPointerGetScreen(inputInfo.pointer)->myNum)
 | |
|                 miPointerSetScreen(inputInfo.pointer, firstScreen->index, x, y);
 | |
| #endif
 | |
|             _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,
 | |
| };
 |