Merged mouse dll into USER.
[wine.git] / windows / x11drv / mouse.c
blobeab97ece084db77192b3eaf9f4917284eaa7449c
1 /*
2 * X11 mouse driver
4 * Copyright 1998 Ulrich Weigand
5 */
7 #include "config.h"
9 #include "ts_xlib.h"
11 #include "callback.h"
12 #include "debugtools.h"
13 #include "mouse.h"
14 #include "monitor.h"
15 #include "win.h"
16 #include "windef.h"
17 #include "x11drv.h"
19 DEFAULT_DEBUG_CHANNEL(cursor);
21 /**********************************************************************/
23 Cursor X11DRV_MOUSE_XCursor = None; /* Current X cursor */
25 static LONG X11DRV_MOUSE_WarpPointer = 0; /* hack; see DISPLAY_MoveCursor */
26 static LPMOUSE_EVENT_PROC DefMouseEventProc = NULL;
28 /***********************************************************************
29 * X11DRV_MOUSE_DoSetCursor
31 static BOOL X11DRV_MOUSE_DoSetCursor( CURSORICONINFO *ptr )
33 Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
34 XColor fg, bg;
35 Cursor cursor = None;
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 );
43 if (pixmapBits)
45 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
46 &bg, &bg, 0, 0 );
47 XFreePixmap( display, pixmapBits );
50 else /* Create the X cursor from the bits */
52 XImage *image;
54 if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
56 WARN("Cursor has more than 1 bpp!\n" );
57 return FALSE;
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
65 * algorithm here.
67 pixmapAll = XCreatePixmap( display, X11DRV_GetXRootWindow(),
68 ptr->nWidth, ptr->nHeight * 2, 1 );
69 image = XCreateImage( display, X11DRV_GetVisual(),
70 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
71 ptr->nHeight * 2, 16, ptr->nWidthBytes);
72 if (image)
74 image->byte_order = MSBFirst;
75 image->bitmap_bit_order = MSBFirst;
76 image->bitmap_unit = 16;
77 _XInitImageFuncPtrs(image);
78 if (pixmapAll)
79 XPutImage( display, pixmapAll, BITMAP_monoGC, image,
80 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
81 image->data = NULL;
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:
105 * Windows | X11
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
112 * which gives:
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;
161 return TRUE;
164 /***********************************************************************
165 * X11DRV_MOUSE_SetCursor
167 void X11DRV_MOUSE_SetCursor( CURSORICONINFO *lpCursor )
169 BOOL success;
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 );
182 else
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 );
190 while(hwnd)
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.
225 Window root, child;
226 int rootX, rootY, winX, winY;
227 unsigned int xstate;
229 if (X11DRV_MOUSE_WarpPointer < 0) return;
231 if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
232 &rootX, &rootY, &winX, &winY, &xstate ))
233 return;
235 if ( winX == wAbsX && winY == wAbsY )
236 return;
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_Init
247 void X11DRV_MOUSE_Init( LPMOUSE_EVENT_PROC proc )
249 static int init_done;
251 DefMouseEventProc = proc;
253 if (!init_done)
255 Window root, child;
256 int root_x, root_y, child_x, child_y;
257 unsigned int KeyState;
259 init_done = 1;
260 /* Get the current mouse position and simulate an absolute mouse
261 movement to initialize the mouse global variables */
262 TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
263 &root_x, &root_y, &child_x, &child_y, &KeyState);
264 X11DRV_MOUSE_SendEvent(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
265 root_x, root_y, X11DRV_EVENT_XStateToKeyState(KeyState),
266 GetTickCount(), 0 );
271 /***********************************************************************
272 * X11DRV_MOUSE_SendEvent
274 void X11DRV_MOUSE_SendEvent( DWORD mouseStatus, DWORD posX, DWORD posY,
275 DWORD keyState, DWORD time, HWND hWnd )
277 int width = MONITOR_GetWidth (&MONITOR_PrimaryMonitor);
278 int height = MONITOR_GetHeight(&MONITOR_PrimaryMonitor);
279 int iWndsLocks;
280 WINE_MOUSEEVENT wme;
282 if ( !DefMouseEventProc ) return;
284 TRACE("(%04lX,%ld,%ld)\n", mouseStatus, posX, posY );
286 if (mouseStatus & MOUSEEVENTF_MOVE) {
287 if (mouseStatus & MOUSEEVENTF_ABSOLUTE) {
288 /* Relative mouse movements seems not to be scaled as absolute ones */
289 posX = (((long)posX << 16) + width-1) / width;
290 posY = (((long)posY << 16) + height-1) / height;
294 wme.magic = WINE_MOUSEEVENT_MAGIC;
295 wme.time = time;
296 wme.hWnd = hWnd;
297 wme.keyState = keyState;
299 InterlockedDecrement( &X11DRV_MOUSE_WarpPointer );
300 /* To avoid deadlocks, we have to suspend all locks on windows structures
301 before the program control is passed to the mouse driver */
302 iWndsLocks = WIN_SuspendWndsLock();
303 DefMouseEventProc( mouseStatus, posX, posY, 0, (DWORD)&wme );
304 WIN_RestoreWndsLock(iWndsLocks);
305 InterlockedIncrement( &X11DRV_MOUSE_WarpPointer );