4 * Copyright 1998 Ulrich Weigand
12 #include "debugtools.h"
18 DEFAULT_DEBUG_CHANNEL(cursor);
20 /**********************************************************************/
22 Cursor X11DRV_MOUSE_XCursor = None; /* Current X cursor */
24 static LONG X11DRV_MOUSE_WarpPointer = 0; /* hack; see DISPLAY_MoveCursor */
25 static LPMOUSE_EVENT_PROC DefMouseEventProc = NULL;
27 /***********************************************************************
28 * X11DRV_MOUSE_DoSetCursor
30 static BOOL X11DRV_MOUSE_DoSetCursor( CURSORICONINFO *ptr )
32 Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
36 if (!ptr) /* Create an empty cursor */
38 static const char data[] = { 0 };
40 bg.red = bg.green = bg.blue = 0x0000;
41 pixmapBits = XCreateBitmapFromData( display, X11DRV_GetXRootWindow(), data, 1, 1 );
44 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
46 XFreePixmap( display, pixmapBits );
49 else /* Create the X cursor from the bits */
53 if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
55 WARN("Cursor has more than 1 bpp!\n" );
59 /* Create a pixmap and transfer all the bits to it */
61 /* NOTE: Following hack works, but only because XFree depth
62 * 1 images really use 1 bit/pixel (and so the same layout
63 * as the Windows cursor data). Perhaps use a more generic
66 pixmapAll = XCreatePixmap( display, X11DRV_GetXRootWindow(),
67 ptr->nWidth, ptr->nHeight * 2, 1 );
68 image = XCreateImage( display, X11DRV_GetVisual(),
69 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
70 ptr->nHeight * 2, 16, ptr->nWidthBytes);
73 image->byte_order = MSBFirst;
74 image->bitmap_bit_order = MSBFirst;
75 image->bitmap_unit = 16;
76 _XInitImageFuncPtrs(image);
78 XPutImage( display, pixmapAll, BITMAP_monoGC, image,
79 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
81 XDestroyImage( image );
84 /* Now create the 2 pixmaps for bits and mask */
86 pixmapBits = XCreatePixmap( display, X11DRV_GetXRootWindow(),
87 ptr->nWidth, ptr->nHeight, 1 );
88 pixmapMask = XCreatePixmap( display, X11DRV_GetXRootWindow(),
89 ptr->nWidth, ptr->nHeight, 1 );
90 pixmapMaskInv = XCreatePixmap( display, X11DRV_GetXRootWindow(),
91 ptr->nWidth, ptr->nHeight, 1 );
93 /* Make sure everything went OK so far */
95 if (pixmapBits && pixmapMask && pixmapAll)
97 /* We have to do some magic here, as cursors are not fully
98 * compatible between Windows and X11. Under X11, there
99 * are only 3 possible color cursor: black, white and
100 * masked. So we map the 4th Windows color (invert the
101 * bits on the screen) to black and an additional white bit on
102 * an other place (+1,+1). This require some boolean arithmetic:
105 * And Xor Result | Bits Mask Result
106 * 0 0 black | 0 1 background
107 * 0 1 white | 1 1 foreground
108 * 1 0 no change | X 0 no change
109 * 1 1 inverted | 0 1 background
112 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
113 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
115 * FIXME: apparently some servers do support 'inverted' color.
116 * I don't know if it's correct per the X spec, but maybe
117 * we ought to take advantage of it. -- AJ
119 XSetFunction( display, BITMAP_monoGC, GXcopy );
120 XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
121 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
122 XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
123 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
124 XCopyArea( display, pixmapAll, pixmapMaskInv, BITMAP_monoGC,
125 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
126 XSetFunction( display, BITMAP_monoGC, GXand );
127 XCopyArea( display, pixmapAll, pixmapMaskInv, BITMAP_monoGC,
128 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
129 XSetFunction( display, BITMAP_monoGC, GXandReverse );
130 XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
131 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
132 XSetFunction( display, BITMAP_monoGC, GXorReverse );
133 XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
134 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
135 /* Additional white */
136 XSetFunction( display, BITMAP_monoGC, GXor );
137 XCopyArea( display, pixmapMaskInv, pixmapMask, BITMAP_monoGC,
138 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
139 XCopyArea( display, pixmapMaskInv, pixmapBits, BITMAP_monoGC,
140 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
141 XSetFunction( display, BITMAP_monoGC, GXcopy );
142 fg.red = fg.green = fg.blue = 0xffff;
143 bg.red = bg.green = bg.blue = 0x0000;
144 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
145 &fg, &bg, ptr->ptHotSpot.x, ptr->ptHotSpot.y );
148 /* Now free everything */
150 if (pixmapAll) XFreePixmap( display, pixmapAll );
151 if (pixmapBits) XFreePixmap( display, pixmapBits );
152 if (pixmapMask) XFreePixmap( display, pixmapMask );
153 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
156 if (cursor == None) return FALSE;
157 if (X11DRV_MOUSE_XCursor != None) XFreeCursor( display, X11DRV_MOUSE_XCursor );
158 X11DRV_MOUSE_XCursor = cursor;
163 /***********************************************************************
166 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
170 EnterCriticalSection( &X11DRV_CritSection );
171 success = CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor, lpCursor );
172 LeaveCriticalSection( &X11DRV_CritSection );
173 if ( !success ) return;
175 if (X11DRV_GetXRootWindow() != DefaultRootWindow(display))
177 /* If in desktop mode, set the cursor on the desktop window */
179 TSXDefineCursor( display, X11DRV_GetXRootWindow(), X11DRV_MOUSE_XCursor );
183 /* Else, set the same cursor for all top-level windows */
185 /* FIXME: we should not reference USER internals here, but native USER
186 works only in desktop mode anyway, so this should not matter */
188 HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
191 WND *tmpWnd = WIN_FindWndPtr(hwnd);
192 Window win = X11DRV_WND_FindXWindow(tmpWnd );
193 if (win && win!=DefaultRootWindow(display))
194 TSXDefineCursor( display, win, X11DRV_MOUSE_XCursor );
195 hwnd = GetWindow( hwnd, GW_HWNDNEXT );
196 WIN_ReleaseWndPtr(tmpWnd);
201 /***********************************************************************
204 void X11DRV_MoveCursor(WORD wAbsX, WORD wAbsY)
207 * We do not want the to create MotionNotify events here,
208 * otherwise we will get an endless recursion:
209 * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
210 * -> XWarpPointer -> XMotionEvent -> ...
212 * Unfortunately, the XWarpPointer call does create a MotionNotify
213 * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
214 * procedure, it sets a global flag. If this flag is set, we skip the
215 * XWarpPointer call. If we are *not* called from within MOUSE_SendEvent,
216 * we will call XWarpPointer, which will create a MotionNotify event.
217 * Strictly speaking, this is also wrong, but that should normally not
218 * have any negative effects ...
220 * But first of all, we check whether we already are at the position
221 * are supposed to move to; if so, we don't need to do anything.
225 int rootX, rootY, winX, winY;
228 if (X11DRV_MOUSE_WarpPointer < 0) return;
230 if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
231 &rootX, &rootY, &winX, &winY, &xstate ))
234 if ( winX == wAbsX && winY == wAbsY )
237 TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
239 TSXWarpPointer( display, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(),
240 0, 0, 0, 0, wAbsX, wAbsY );
243 /***********************************************************************
246 void X11DRV_InitMouse( LPMOUSE_EVENT_PROC proc )
248 static int init_done;
250 DefMouseEventProc = proc;
255 int root_x, root_y, child_x, child_y;
256 unsigned int KeyState;
259 /* Get the current mouse position and simulate an absolute mouse
260 movement to initialize the mouse global variables */
261 TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
262 &root_x, &root_y, &child_x, &child_y, &KeyState);
263 X11DRV_SendEvent(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
264 root_x, root_y, X11DRV_EVENT_XStateToKeyState(KeyState),
270 /***********************************************************************
273 void X11DRV_SendEvent( DWORD mouseStatus, DWORD posX, DWORD posY,
274 DWORD keyState, DWORD time, HWND hWnd )
276 int width = GetSystemMetrics( SM_CXSCREEN );
277 int height = GetSystemMetrics( SM_CYSCREEN );
281 if ( !DefMouseEventProc ) return;
283 TRACE("(%04lX,%ld,%ld)\n", mouseStatus, posX, posY );
285 if (mouseStatus & MOUSEEVENTF_MOVE) {
286 if (mouseStatus & MOUSEEVENTF_ABSOLUTE) {
287 /* Relative mouse movements seems not to be scaled as absolute ones */
288 posX = (((long)posX << 16) + width-1) / width;
289 posY = (((long)posY << 16) + height-1) / height;
293 wme.magic = WINE_MOUSEEVENT_MAGIC;
296 wme.keyState = keyState;
298 InterlockedDecrement( &X11DRV_MOUSE_WarpPointer );
299 /* To avoid deadlocks, we have to suspend all locks on windows structures
300 before the program control is passed to the mouse driver */
301 iWndsLocks = WIN_SuspendWndsLock();
302 DefMouseEventProc( mouseStatus, posX, posY, 0, (DWORD)&wme );
303 WIN_RestoreWndsLock(iWndsLocks);
304 InterlockedIncrement( &X11DRV_MOUSE_WarpPointer );