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 WND *wndPtr = WIN_FindWndPtr(hwnd);
160 Window win = X11DRV_WND_GetXWindow(wndPtr);
161 if (win) TSXDefineCursor( thread_display(), win, (Cursor)cursor );
163 WIN_ReleaseWndPtr( wndPtr );
167 /***********************************************************************
168 * SetCursor (X11DRV.@)
170 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
174 if (root_window != DefaultRootWindow(gdi_display))
176 /* If in desktop mode, set the cursor on the desktop window */
179 cursor = X11DRV_GetCursor( gdi_display, lpCursor );
182 XDefineCursor( gdi_display, root_window, cursor );
183 XFreeCursor( gdi_display, cursor );
187 else /* set the same cursor for all top-level windows of the current thread */
189 Display *display = thread_display();
192 cursor = X11DRV_GetCursor( display, lpCursor );
196 /* EnumThreadWindows( GetCurrentThreadId(), set_win_cursor, (LPARAM)cursor );*/
197 EnumWindows( set_win_cursor, (LPARAM)cursor );
198 TSXFreeCursor( display, cursor );
203 /***********************************************************************
204 * MoveCursor (X11DRV.@)
206 void X11DRV_MoveCursor(WORD wAbsX, WORD wAbsY)
209 * We do not want to create MotionNotify events here,
210 * otherwise we will get an endless recursion:
211 * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
212 * -> XWarpPointer -> XMotionEvent -> ...
214 * Unfortunately, the XWarpPointer call does create a MotionNotify
215 * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
216 * procedure, it sets a global flag. If this flag is set, we skip the
217 * XWarpPointer call. If we are *not* called from within MOUSE_SendEvent,
218 * we will call XWarpPointer, which will create a MotionNotify event.
219 * Strictly speaking, this is also wrong, but that should normally not
220 * have any negative effects ...
222 * But first of all, we check whether we already are at the position
223 * are supposed to move to; if so, we don't need to do anything.
226 Display *display = thread_display();
228 int rootX, rootY, winX, winY;
231 if (X11DRV_MOUSE_WarpPointer < 0) return;
233 if (!TSXQueryPointer( display, root_window, &root, &child,
234 &rootX, &rootY, &winX, &winY, &xstate ))
237 if ( winX == wAbsX && winY == wAbsY )
240 TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
242 TSXWarpPointer( display, root_window, root_window, 0, 0, 0, 0, wAbsX, wAbsY );
245 /***********************************************************************
246 * InitMouse (X11DRV.@)
248 void X11DRV_InitMouse( LPMOUSE_EVENT_PROC proc )
250 static int init_done;
252 DefMouseEventProc = proc;
257 int root_x, root_y, child_x, child_y;
258 unsigned int KeyState;
261 /* Get the current mouse position and simulate an absolute mouse
262 movement to initialize the mouse global variables */
263 TSXQueryPointer( thread_display(), root_window, &root, &child,
264 &root_x, &root_y, &child_x, &child_y, &KeyState);
265 X11DRV_SendEvent(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
266 root_x, root_y, X11DRV_EVENT_XStateToKeyState(KeyState),
272 /***********************************************************************
273 * X11DRV_SendEvent (internal)
275 void X11DRV_SendEvent( DWORD mouseStatus, DWORD posX, DWORD posY,
276 DWORD keyState, DWORD time, HWND hWnd )
278 int width = GetSystemMetrics( SM_CXSCREEN );
279 int height = GetSystemMetrics( SM_CYSCREEN );
283 if ( !DefMouseEventProc ) return;
285 TRACE("(%04lX,%ld,%ld)\n", mouseStatus, posX, posY );
287 if (mouseStatus & MOUSEEVENTF_MOVE) {
288 if (mouseStatus & MOUSEEVENTF_ABSOLUTE) {
289 /* Relative mouse movements seems not to be scaled as absolute ones */
290 posX = (((long)posX << 16) + width-1) / width;
291 posY = (((long)posY << 16) + height-1) / height;
295 wme.magic = WINE_MOUSEEVENT_MAGIC;
298 wme.keyState = keyState;
300 InterlockedDecrement( &X11DRV_MOUSE_WarpPointer );
301 /* To avoid deadlocks, we have to suspend all locks on windows structures
302 before the program control is passed to the mouse driver */
303 iWndsLocks = WIN_SuspendWndsLock();
304 DefMouseEventProc( mouseStatus, posX, posY, 0, (DWORD)&wme );
305 WIN_RestoreWndsLock(iWndsLocks);
306 InterlockedIncrement( &X11DRV_MOUSE_WarpPointer );