4 * Copyright 1998 Ulrich Weigand
9 #ifndef X_DISPLAY_MISSING
14 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(cursor)
22 /**********************************************************************/
24 Cursor X11DRV_MOUSE_XCursor = None; /* Current X cursor */
26 static BOOL X11DRV_MOUSE_WarpPointer = TRUE; /* hack; see DISPLAY_MoveCursor */
28 /***********************************************************************
29 * X11DRV_MOUSE_DoSetCursor
31 static BOOL X11DRV_MOUSE_DoSetCursor( CURSORICONINFO *ptr )
33 Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
37 if (!ptr) /* Create an empty cursor */
39 static const char data[] = { 0 };
41 bg.red = bg.green = bg.blue = 0x0000;
42 pixmapBits = XCreateBitmapFromData( display, X11DRV_GetXRootWindow(), data, 1, 1 );
45 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
47 XFreePixmap( display, pixmapBits );
50 else /* Create the X cursor from the bits */
54 if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
56 WARN("Cursor has more than 1 bpp!\n" );
60 /* Create a pixmap and transfer all the bits to it */
62 /* NOTE: Following hack works, but only because XFree depth
63 * 1 images really use 1 bit/pixel (and so the same layout
64 * as the Windows cursor data). Perhaps use a more generic
67 pixmapAll = XCreatePixmap( display, X11DRV_GetXRootWindow(),
68 ptr->nWidth, ptr->nHeight * 2, 1 );
69 image = XCreateImage( display, DefaultVisualOfScreen(X11DRV_GetXScreen()),
70 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
71 ptr->nHeight * 2, 16, ptr->nWidthBytes);
74 image->byte_order = MSBFirst;
75 image->bitmap_bit_order = MSBFirst;
76 image->bitmap_unit = 16;
77 _XInitImageFuncPtrs(image);
79 XPutImage( display, pixmapAll, BITMAP_monoGC, image,
80 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
82 XDestroyImage( image );
85 /* Now create the 2 pixmaps for bits and mask */
87 pixmapBits = XCreatePixmap( display, X11DRV_GetXRootWindow(),
88 ptr->nWidth, ptr->nHeight, 1 );
89 pixmapMask = XCreatePixmap( display, X11DRV_GetXRootWindow(),
90 ptr->nWidth, ptr->nHeight, 1 );
91 pixmapMaskInv = XCreatePixmap( display, X11DRV_GetXRootWindow(),
92 ptr->nWidth, ptr->nHeight, 1 );
94 /* Make sure everything went OK so far */
96 if (pixmapBits && pixmapMask && pixmapAll)
98 /* We have to do some magic here, as cursors are not fully
99 * compatible between Windows and X11. Under X11, there
100 * are only 3 possible color cursor: black, white and
101 * masked. So we map the 4th Windows color (invert the
102 * bits on the screen) to black and an additional white bit on
103 * an other place (+1,+1). This require some boolean arithmetic:
106 * And Xor Result | Bits Mask Result
107 * 0 0 black | 0 1 background
108 * 0 1 white | 1 1 foreground
109 * 1 0 no change | X 0 no change
110 * 1 1 inverted | 0 1 background
113 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
114 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
116 * FIXME: apparently some servers do support 'inverted' color.
117 * I don't know if it's correct per the X spec, but maybe
118 * we ought to take advantage of it. -- AJ
120 XSetFunction( display, BITMAP_monoGC, GXcopy );
121 XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
122 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
123 XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
124 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
125 XCopyArea( display, pixmapAll, pixmapMaskInv, BITMAP_monoGC,
126 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
127 XSetFunction( display, BITMAP_monoGC, GXand );
128 XCopyArea( display, pixmapAll, pixmapMaskInv, BITMAP_monoGC,
129 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
130 XSetFunction( display, BITMAP_monoGC, GXandReverse );
131 XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
132 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
133 XSetFunction( display, BITMAP_monoGC, GXorReverse );
134 XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
135 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
136 /* Additional white */
137 XSetFunction( display, BITMAP_monoGC, GXor );
138 XCopyArea( display, pixmapMaskInv, pixmapMask, BITMAP_monoGC,
139 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
140 XCopyArea( display, pixmapMaskInv, pixmapBits, BITMAP_monoGC,
141 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
142 XSetFunction( display, BITMAP_monoGC, GXcopy );
143 fg.red = fg.green = fg.blue = 0xffff;
144 bg.red = bg.green = bg.blue = 0x0000;
145 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
146 &fg, &bg, ptr->ptHotSpot.x, ptr->ptHotSpot.y );
149 /* Now free everything */
151 if (pixmapAll) XFreePixmap( display, pixmapAll );
152 if (pixmapBits) XFreePixmap( display, pixmapBits );
153 if (pixmapMask) XFreePixmap( display, pixmapMask );
154 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
157 if (cursor == None) return FALSE;
158 if (X11DRV_MOUSE_XCursor != None) XFreeCursor( display, X11DRV_MOUSE_XCursor );
159 X11DRV_MOUSE_XCursor = cursor;
164 /***********************************************************************
165 * X11DRV_MOUSE_SetCursor
167 void X11DRV_MOUSE_SetCursor( CURSORICONINFO *lpCursor )
171 EnterCriticalSection( &X11DRV_CritSection );
172 success = CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor, lpCursor );
173 LeaveCriticalSection( &X11DRV_CritSection );
174 if ( !success ) return;
176 if (X11DRV_GetXRootWindow() != DefaultRootWindow(display))
178 /* If in desktop mode, set the cursor on the desktop window */
180 TSXDefineCursor( display, X11DRV_GetXRootWindow(), X11DRV_MOUSE_XCursor );
184 /* Else, set the same cursor for all top-level windows */
186 /* FIXME: we should not reference USER internals here, but native USER
187 works only in desktop mode anyway, so this should not matter */
189 HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
192 WND *tmpWnd = WIN_FindWndPtr(hwnd);
193 Window win = X11DRV_WND_FindXWindow(tmpWnd );
194 if (win && win!=DefaultRootWindow(display))
195 TSXDefineCursor( display, win, X11DRV_MOUSE_XCursor );
196 hwnd = GetWindow( hwnd, GW_HWNDNEXT );
197 WIN_ReleaseWndPtr(tmpWnd);
202 /***********************************************************************
203 * X11DRV_MOUSE_MoveCursor
205 void X11DRV_MOUSE_MoveCursor(WORD wAbsX, WORD wAbsY)
208 * We do not want the to create MotionNotify events here,
209 * otherwise we will get an endless recursion:
210 * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
211 * -> XWarpPointer -> XMotionEvent -> ...
213 * Unfortunately, the XWarpPointer call does create a MotionNotify
214 * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
215 * procedure, it sets a global flag. If this flag is set, we skip the
216 * XWarpPointer call. If we are *not* called from within MOUSE_SendEvent,
217 * we will call XWarpPointer, which will create a MotionNotify event.
218 * Strictly speaking, this is also wrong, but that should normally not
219 * have any negative effects ...
221 * But first of all, we check whether we already are at the position
222 * are supposed to move to; if so, we don't need to do anything.
226 int rootX, rootY, winX, winY;
229 if (!X11DRV_MOUSE_WarpPointer) return;
231 if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
232 &rootX, &rootY, &winX, &winY, &xstate ))
235 if ( winX == wAbsX && winY == wAbsY )
238 TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
240 TSXWarpPointer( display, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(),
241 0, 0, 0, 0, wAbsX, wAbsY );
244 /***********************************************************************
245 * X11DRV_MOUSE_EnableWarpPointer
247 BOOL X11DRV_MOUSE_EnableWarpPointer(BOOL bEnable)
249 BOOL bOldEnable = X11DRV_MOUSE_WarpPointer;
251 X11DRV_MOUSE_WarpPointer = bEnable;
256 /***********************************************************************
259 void X11DRV_MOUSE_Init()
262 int root_x, root_y, child_x, child_y;
263 unsigned int KeyState;
265 /* Get the current mouse position and simulate an absolute mouse
266 movement to initialize the mouse global variables */
267 TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
268 &root_x, &root_y, &child_x, &child_y, &KeyState);
270 MOUSE_SendEvent(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
272 X11DRV_EVENT_XStateToKeyState(KeyState),
278 #endif /* !defined(X_DISPLAY_MISSING) */