4 * Copyright 1998 Ulrich Weigand
12 #include "wine/winuser16.h"
14 #include "debugtools.h"
19 DEFAULT_DEBUG_CHANNEL(cursor);
21 /**********************************************************************/
23 static LONG X11DRV_MOUSE_WarpPointer = 0; /* hack; see DISPLAY_MoveCursor */
24 static LPMOUSE_EVENT_PROC DefMouseEventProc = NULL;
26 /***********************************************************************
29 Cursor X11DRV_GetCursor( Display *display, CURSORICONINFO *ptr )
31 Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
35 if (!ptr) /* Create an empty cursor */
37 static const char data[] = { 0 };
39 bg.red = bg.green = bg.blue = 0x0000;
40 pixmapBits = XCreateBitmapFromData( display, root_window, data, 1, 1 );
43 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
45 XFreePixmap( display, pixmapBits );
48 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 if (!(pixmapAll = XCreatePixmap( display, root_window,
67 ptr->nWidth, ptr->nHeight * 2, 1 ))) return 0;
68 if (!(image = XCreateImage( display, visual,
69 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
70 ptr->nHeight * 2, 16, ptr->nWidthBytes))) return 0;
71 gc = XCreateGC( display, pixmapAll, 0, NULL );
72 XSetGraphicsExposures( display, gc, False );
73 image->byte_order = MSBFirst;
74 image->bitmap_bit_order = MSBFirst;
75 image->bitmap_unit = 16;
76 _XInitImageFuncPtrs(image);
77 XPutImage( display, pixmapAll, gc, image,
78 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
80 XDestroyImage( image );
82 /* Now create the 2 pixmaps for bits and mask */
84 pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
85 pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
86 pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
88 /* Make sure everything went OK so far */
90 if (pixmapBits && pixmapMask && pixmapMaskInv)
92 /* We have to do some magic here, as cursors are not fully
93 * compatible between Windows and X11. Under X11, there
94 * are only 3 possible color cursor: black, white and
95 * masked. So we map the 4th Windows color (invert the
96 * bits on the screen) to black and an additional white bit on
97 * an other place (+1,+1). This require some boolean arithmetic:
100 * And Xor Result | Bits Mask Result
101 * 0 0 black | 0 1 background
102 * 0 1 white | 1 1 foreground
103 * 1 0 no change | X 0 no change
104 * 1 1 inverted | 0 1 background
107 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
108 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
110 * FIXME: apparently some servers do support 'inverted' color.
111 * I don't know if it's correct per the X spec, but maybe
112 * we ought to take advantage of it. -- AJ
114 XSetFunction( display, gc, GXcopy );
115 XCopyArea( display, pixmapAll, pixmapBits, gc,
116 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
117 XCopyArea( display, pixmapAll, pixmapMask, gc,
118 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
119 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
120 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
121 XSetFunction( display, gc, GXand );
122 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
123 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
124 XSetFunction( display, gc, GXandReverse );
125 XCopyArea( display, pixmapAll, pixmapBits, gc,
126 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
127 XSetFunction( display, gc, GXorReverse );
128 XCopyArea( display, pixmapAll, pixmapMask, gc,
129 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
130 /* Additional white */
131 XSetFunction( display, gc, GXor );
132 XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
133 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
134 XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
135 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
136 XSetFunction( display, gc, GXcopy );
137 fg.red = fg.green = fg.blue = 0xffff;
138 bg.red = bg.green = bg.blue = 0x0000;
139 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
140 &fg, &bg, ptr->ptHotSpot.x, ptr->ptHotSpot.y );
143 /* Now free everything */
145 if (pixmapAll) XFreePixmap( display, pixmapAll );
146 if (pixmapBits) XFreePixmap( display, pixmapBits );
147 if (pixmapMask) XFreePixmap( display, pixmapMask );
148 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
149 XFreeGC( display, gc );
154 /* set the cursor of a window; helper for X11DRV_SetCursor */
155 static BOOL CALLBACK set_win_cursor( HWND hwnd, LPARAM cursor )
157 Window win = X11DRV_get_whole_window( hwnd );
158 if (win) TSXDefineCursor( thread_display(), win, (Cursor)cursor );
162 /***********************************************************************
163 * SetCursor (X11DRV.@)
165 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
169 if (root_window != DefaultRootWindow(gdi_display))
171 /* If in desktop mode, set the cursor on the desktop window */
174 cursor = X11DRV_GetCursor( gdi_display, lpCursor );
177 XDefineCursor( gdi_display, root_window, cursor );
178 XFreeCursor( gdi_display, cursor );
182 else /* set the same cursor for all top-level windows of the current thread */
184 Display *display = thread_display();
187 cursor = X11DRV_GetCursor( display, lpCursor );
191 /* EnumThreadWindows( GetCurrentThreadId(), set_win_cursor, (LPARAM)cursor );*/
192 EnumWindows( set_win_cursor, (LPARAM)cursor );
193 TSXFreeCursor( display, cursor );
198 /***********************************************************************
199 * SetCursorPos (X11DRV.@)
201 void X11DRV_SetCursorPos(INT wAbsX, INT wAbsY)
204 * We do not want to create MotionNotify events here,
205 * otherwise we will get an endless recursion:
206 * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
207 * -> XWarpPointer -> XMotionEvent -> ...
209 * Unfortunately, the XWarpPointer call does create a MotionNotify
210 * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
211 * procedure, it sets a global flag. If this flag is set, we skip the
212 * XWarpPointer call. If we are *not* called from within MOUSE_SendEvent,
213 * we will call XWarpPointer, which will create a MotionNotify event.
214 * Strictly speaking, this is also wrong, but that should normally not
215 * have any negative effects ...
217 * But first of all, we check whether we already are at the position
218 * are supposed to move to; if so, we don't need to do anything.
221 Display *display = thread_display();
223 int rootX, rootY, winX, winY;
226 if (X11DRV_MOUSE_WarpPointer < 0) return;
228 if (!TSXQueryPointer( display, root_window, &root, &child,
229 &rootX, &rootY, &winX, &winY, &xstate ))
232 if ( winX == wAbsX && winY == wAbsY )
235 TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
238 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0, wAbsX, wAbsY );
239 XFlush( display ); /* just in case */
243 /***********************************************************************
244 * GetCursorPos (X11DRV.@)
246 void X11DRV_GetCursorPos(LPPOINT pos)
248 Display *display = thread_display();
250 int rootX, rootY, winX, winY;
253 if (!TSXQueryPointer( display, root_window, &root, &child,
254 &rootX, &rootY, &winX, &winY, &xstate ))
257 TRACE("pointer at (%d,%d)\n", winX, winY );
262 /***********************************************************************
263 * InitMouse (X11DRV.@)
265 void X11DRV_InitMouse( LPMOUSE_EVENT_PROC proc )
267 static int init_done;
269 DefMouseEventProc = proc;
274 int root_x, root_y, child_x, child_y;
275 unsigned int KeyState;
278 /* Get the current mouse position and simulate an absolute mouse
279 movement to initialize the mouse global variables */
280 TSXQueryPointer( thread_display(), root_window, &root, &child,
281 &root_x, &root_y, &child_x, &child_y, &KeyState);
282 X11DRV_SendEvent(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
283 root_x, root_y, X11DRV_EVENT_XStateToKeyState(KeyState),
284 0, GetTickCount(), 0 );
289 /***********************************************************************
290 * X11DRV_SendEvent (internal)
292 void X11DRV_SendEvent( DWORD mouseStatus, DWORD posX, DWORD posY,
293 WORD keyState, DWORD data, DWORD time, HWND hWnd )
295 int width = GetSystemMetrics( SM_CXSCREEN );
296 int height = GetSystemMetrics( SM_CYSCREEN );
300 if ( !DefMouseEventProc ) return;
302 TRACE("(%04lX,%ld,%ld)\n", mouseStatus, posX, posY );
304 if (mouseStatus & MOUSEEVENTF_MOVE) {
305 if (mouseStatus & MOUSEEVENTF_ABSOLUTE) {
306 /* Relative mouse movements seems not to be scaled as absolute ones */
307 posX = (((long)posX << 16) + width-1) / width;
308 posY = (((long)posY << 16) + height-1) / height;
312 wme.magic = WINE_MOUSEEVENT_MAGIC;
315 wme.keyState = keyState;
317 InterlockedDecrement( &X11DRV_MOUSE_WarpPointer );
318 /* To avoid deadlocks, we have to suspend all locks on windows structures
319 before the program control is passed to the mouse driver */
320 iWndsLocks = WIN_SuspendWndsLock();
321 DefMouseEventProc( mouseStatus, posX, posY, data, (DWORD)&wme );
322 WIN_RestoreWndsLock(iWndsLocks);
323 InterlockedIncrement( &X11DRV_MOUSE_WarpPointer );