Added the possibility to have mouse movements reported relative to
[wine] / windows / x11drv / mouse.c
1 /*
2  * X11 mouse driver
3  *
4  * Copyright 1998 Ulrich Weigand
5  */
6
7 #include "config.h"
8
9 #ifndef X_DISPLAY_MISSING
10
11 #include "ts_xlib.h"
12
13 #include "callback.h"
14 #include "debugtools.h"
15 #include "mouse.h"
16 #include "win.h"
17 #include "windef.h"
18 #include "x11drv.h"
19
20 DEFAULT_DEBUG_CHANNEL(cursor)
21
22 /**********************************************************************/
23
24 Cursor X11DRV_MOUSE_XCursor = None;    /* Current X cursor */
25
26 static BOOL X11DRV_MOUSE_WarpPointer = TRUE;  /* hack; see DISPLAY_MoveCursor */
27
28 /***********************************************************************
29  *              X11DRV_MOUSE_DoSetCursor
30  */
31 static BOOL X11DRV_MOUSE_DoSetCursor( CURSORICONINFO *ptr )
32 {
33     Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
34     XColor fg, bg;
35     Cursor cursor = None;
36
37     if (!ptr)  /* Create an empty cursor */
38     {
39         static const char data[] = { 0 };
40
41         bg.red = bg.green = bg.blue = 0x0000;
42         pixmapBits = XCreateBitmapFromData( display, X11DRV_GetXRootWindow(), data, 1, 1 );
43         if (pixmapBits)
44         {
45             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
46                                           &bg, &bg, 0, 0 );
47             XFreePixmap( display, pixmapBits );
48         }
49     }
50     else  /* Create the X cursor from the bits */
51     {
52         XImage *image;
53
54         if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
55         {
56             WARN("Cursor has more than 1 bpp!\n" );
57             return FALSE;
58         }
59
60         /* Create a pixmap and transfer all the bits to it */
61
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.
66          */
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);
72         if (image)
73         {
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 );
83         }
84
85         /* Now create the 2 pixmaps for bits and mask */
86
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 );
93
94         /* Make sure everything went OK so far */
95
96         if (pixmapBits && pixmapMask && pixmapAll)
97         {
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:
104              *
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
111              *
112              * which gives:
113              *  Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
114              *  Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
115              *
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
119              */
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 );
147         }
148
149         /* Now free everything */
150
151         if (pixmapAll) XFreePixmap( display, pixmapAll );
152         if (pixmapBits) XFreePixmap( display, pixmapBits );
153         if (pixmapMask) XFreePixmap( display, pixmapMask );
154         if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
155     }
156
157     if (cursor == None) return FALSE;
158     if (X11DRV_MOUSE_XCursor != None) XFreeCursor( display, X11DRV_MOUSE_XCursor );
159     X11DRV_MOUSE_XCursor = cursor;
160
161     return TRUE;
162 }
163
164 /***********************************************************************
165  *              X11DRV_MOUSE_SetCursor
166  */
167 void X11DRV_MOUSE_SetCursor( CURSORICONINFO *lpCursor )
168 {
169     BOOL success;
170
171     EnterCriticalSection( &X11DRV_CritSection );
172     success = CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor, lpCursor );
173     LeaveCriticalSection( &X11DRV_CritSection );
174     if ( !success ) return;
175
176     if (X11DRV_GetXRootWindow() != DefaultRootWindow(display))
177     {
178         /* If in desktop mode, set the cursor on the desktop window */
179
180         TSXDefineCursor( display, X11DRV_GetXRootWindow(), X11DRV_MOUSE_XCursor );
181     }
182     else
183     {
184         /* Else, set the same cursor for all top-level windows */
185
186         /* FIXME: we should not reference USER internals here, but native USER 
187                   works only in desktop mode anyway, so this should not matter */
188
189         HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
190         while(hwnd)
191         {
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);
198         }
199     }
200 }
201
202 /***********************************************************************
203  *              X11DRV_MOUSE_MoveCursor
204  */
205 void X11DRV_MOUSE_MoveCursor(WORD wAbsX, WORD wAbsY)
206 {
207   /* 
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 -> ...
212    *
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 ...
220    *
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.
223    */
224   
225   Window root, child;
226   int rootX, rootY, winX, winY;
227   unsigned int xstate;
228   
229   if (!X11DRV_MOUSE_WarpPointer) return;
230
231   if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
232                         &rootX, &rootY, &winX, &winY, &xstate ))
233     return;
234   
235   if ( winX == wAbsX && winY == wAbsY )
236     return;
237   
238   TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
239   
240   TSXWarpPointer( display, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(), 
241                   0, 0, 0, 0, wAbsX, wAbsY );
242 }
243
244 /***********************************************************************
245  *           X11DRV_MOUSE_EnableWarpPointer
246  */
247 BOOL X11DRV_MOUSE_EnableWarpPointer(BOOL bEnable)
248 {
249   BOOL bOldEnable = X11DRV_MOUSE_WarpPointer;
250
251   X11DRV_MOUSE_WarpPointer = bEnable;
252
253   return bOldEnable;
254 }
255
256 /***********************************************************************
257  *           X11DRV_MOUSE_Init
258  */
259 void X11DRV_MOUSE_Init()
260 {
261   Window root, child;
262   int root_x, root_y, child_x, child_y;
263   unsigned int KeyState;
264   
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);
269
270   MOUSE_SendEvent(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
271                   root_x, root_y,
272                   X11DRV_EVENT_XStateToKeyState(KeyState),
273                   GetTickCount(),
274                   0);
275 }
276
277
278 #endif /* !defined(X_DISPLAY_MISSING) */