Fixed ToUnicode TRACE message.
[wine] / windows / x11drv / mouse.c
1 /*
2  * X11 mouse driver
3  *
4  * Copyright 1998 Ulrich Weigand
5  */
6
7 #include "config.h"
8
9 #include "ts_xlib.h"
10
11 #include "callback.h"
12 #include "debugtools.h"
13 #include "mouse.h"
14 #include "win.h"
15 #include "windef.h"
16 #include "x11drv.h"
17
18 DEFAULT_DEBUG_CHANNEL(cursor);
19
20 /**********************************************************************/
21
22 Cursor X11DRV_MOUSE_XCursor = None;    /* Current X cursor */
23
24 static LONG X11DRV_MOUSE_WarpPointer = 0;  /* hack; see DISPLAY_MoveCursor */
25 static LPMOUSE_EVENT_PROC DefMouseEventProc = NULL;
26
27 /***********************************************************************
28  *              X11DRV_MOUSE_DoSetCursor
29  */
30 static BOOL X11DRV_MOUSE_DoSetCursor( CURSORICONINFO *ptr )
31 {
32     Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
33     XColor fg, bg;
34     Cursor cursor = None;
35
36     if (!ptr)  /* Create an empty cursor */
37     {
38         static const char data[] = { 0 };
39
40         bg.red = bg.green = bg.blue = 0x0000;
41         pixmapBits = XCreateBitmapFromData( display, X11DRV_GetXRootWindow(), data, 1, 1 );
42         if (pixmapBits)
43         {
44             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
45                                           &bg, &bg, 0, 0 );
46             XFreePixmap( display, pixmapBits );
47         }
48     }
49     else  /* Create the X cursor from the bits */
50     {
51         XImage *image;
52
53         if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
54         {
55             WARN("Cursor has more than 1 bpp!\n" );
56             return FALSE;
57         }
58
59         /* Create a pixmap and transfer all the bits to it */
60
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
64          *       algorithm here.
65          */
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);
71         if (image)
72         {
73             image->byte_order = MSBFirst;
74             image->bitmap_bit_order = MSBFirst;
75             image->bitmap_unit = 16;
76             _XInitImageFuncPtrs(image);
77             if (pixmapAll)
78                 XPutImage( display, pixmapAll, BITMAP_monoGC, image,
79                            0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
80             image->data = NULL;
81             XDestroyImage( image );
82         }
83
84         /* Now create the 2 pixmaps for bits and mask */
85
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 );
92
93         /* Make sure everything went OK so far */
94
95         if (pixmapBits && pixmapMask && pixmapAll)
96         {
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:
103              *
104              *         Windows          |          X11
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
110              *
111              * which gives:
112              *  Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
113              *  Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
114              *
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
118              */
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 );
146         }
147
148         /* Now free everything */
149
150         if (pixmapAll) XFreePixmap( display, pixmapAll );
151         if (pixmapBits) XFreePixmap( display, pixmapBits );
152         if (pixmapMask) XFreePixmap( display, pixmapMask );
153         if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
154     }
155
156     if (cursor == None) return FALSE;
157     if (X11DRV_MOUSE_XCursor != None) XFreeCursor( display, X11DRV_MOUSE_XCursor );
158     X11DRV_MOUSE_XCursor = cursor;
159
160     return TRUE;
161 }
162
163 /***********************************************************************
164  *              X11DRV_SetCursor
165  */
166 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
167 {
168     BOOL success;
169
170     EnterCriticalSection( &X11DRV_CritSection );
171     success = CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor, lpCursor );
172     LeaveCriticalSection( &X11DRV_CritSection );
173     if ( !success ) return;
174
175     if (X11DRV_GetXRootWindow() != DefaultRootWindow(display))
176     {
177         /* If in desktop mode, set the cursor on the desktop window */
178
179         TSXDefineCursor( display, X11DRV_GetXRootWindow(), X11DRV_MOUSE_XCursor );
180     }
181     else
182     {
183         /* Else, set the same cursor for all top-level windows */
184
185         /* FIXME: we should not reference USER internals here, but native USER 
186                   works only in desktop mode anyway, so this should not matter */
187
188         HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
189         while(hwnd)
190         {
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);
197         }
198     }
199 }
200
201 /***********************************************************************
202  *              X11DRV_MoveCursor
203  */
204 void X11DRV_MoveCursor(WORD wAbsX, WORD wAbsY)
205 {
206   /* 
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 -> ...
211    *
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 ...
219    *
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.
222    */
223   
224   Window root, child;
225   int rootX, rootY, winX, winY;
226   unsigned int xstate;
227   
228   if (X11DRV_MOUSE_WarpPointer < 0) return;
229
230   if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
231                         &rootX, &rootY, &winX, &winY, &xstate ))
232     return;
233   
234   if ( winX == wAbsX && winY == wAbsY )
235     return;
236   
237   TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
238   
239   TSXWarpPointer( display, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(), 
240                   0, 0, 0, 0, wAbsX, wAbsY );
241 }
242
243 /***********************************************************************
244  *           X11DRV_InitMouse
245  */
246 void X11DRV_InitMouse( LPMOUSE_EVENT_PROC proc )
247 {
248     static int init_done;
249
250     DefMouseEventProc = proc;
251
252     if (!init_done)
253     {
254         Window root, child;
255         int root_x, root_y, child_x, child_y;
256         unsigned int KeyState;
257   
258         init_done = 1;
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),
265                                GetTickCount(), 0 );
266     }
267 }
268
269
270 /***********************************************************************
271  *           X11DRV_SendEvent
272  */
273 void X11DRV_SendEvent( DWORD mouseStatus, DWORD posX, DWORD posY, 
274                              DWORD keyState, DWORD time, HWND hWnd )
275 {
276     int width  = GetSystemMetrics( SM_CXSCREEN );
277     int height = GetSystemMetrics( SM_CYSCREEN );
278     int iWndsLocks;
279     WINE_MOUSEEVENT wme;
280
281     if ( !DefMouseEventProc ) return;
282
283     TRACE("(%04lX,%ld,%ld)\n", mouseStatus, posX, posY );
284
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;
290       }
291     }
292
293     wme.magic    = WINE_MOUSEEVENT_MAGIC;
294     wme.time     = time;
295     wme.hWnd     = hWnd;
296     wme.keyState = keyState;
297     
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 );
305 }