Made access to the wnd struct thread-safe.
[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 "debug.h"
15 #include "display.h"
16 #include "win.h"
17 #include "windef.h"
18 #include "x11drv.h"
19
20 /**********************************************************************/
21
22 Cursor X11DRV_MOUSE_XCursor = None;    /* Current X cursor */
23
24 BOOL X11DRV_MOUSE_DisableWarpPointer = FALSE;  /* hack; see DISPLAY_MoveCursor */
25
26 /***********************************************************************
27  *              X11DRV_MOUSE_DoSetCursor
28  */
29 static BOOL X11DRV_MOUSE_DoSetCursor( CURSORICONINFO *ptr )
30 {
31     Pixmap pixmapBits, pixmapMask, pixmapAll;
32     XColor fg, bg;
33     Cursor cursor = None;
34
35     if (!ptr)  /* Create an empty cursor */
36     {
37         static const char data[] = { 0 };
38
39         bg.red = bg.green = bg.blue = 0x0000;
40         pixmapBits = XCreateBitmapFromData( display, X11DRV_GetXRootWindow(), data, 1, 1 );
41         if (pixmapBits)
42         {
43             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
44                                           &bg, &bg, 0, 0 );
45             XFreePixmap( display, pixmapBits );
46         }
47     }
48     else  /* Create the X cursor from the bits */
49     {
50         XImage *image;
51
52         if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
53         {
54             WARN(cursor, "Cursor has more than 1 bpp!\n" );
55             return FALSE;
56         }
57
58         /* Create a pixmap and transfer all the bits to it */
59
60         /* NOTE: Following hack works, but only because XFree depth
61          *       1 images really use 1 bit/pixel (and so the same layout
62          *       as the Windows cursor data). Perhaps use a more generic
63          *       algorithm here.
64          */
65         pixmapAll = XCreatePixmap( display, X11DRV_GetXRootWindow(),
66                                    ptr->nWidth, ptr->nHeight * 2, 1 );
67         image = XCreateImage( display, DefaultVisualOfScreen(X11DRV_GetXScreen()),
68                               1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
69                               ptr->nHeight * 2, 16, ptr->nWidthBytes);
70         if (image)
71         {
72             image->byte_order = MSBFirst;
73             image->bitmap_bit_order = MSBFirst;
74             image->bitmap_unit = 16;
75             _XInitImageFuncPtrs(image);
76             if (pixmapAll)
77                 XPutImage( display, pixmapAll, BITMAP_monoGC, image,
78                            0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
79             image->data = NULL;
80             XDestroyImage( image );
81         }
82
83         /* Now create the 2 pixmaps for bits and mask */
84
85         pixmapBits = XCreatePixmap( display, X11DRV_GetXRootWindow(),
86                                     ptr->nWidth, ptr->nHeight, 1 );
87         pixmapMask = XCreatePixmap( display, X11DRV_GetXRootWindow(),
88                                     ptr->nWidth, ptr->nHeight, 1 );
89
90         /* Make sure everything went OK so far */
91
92         if (pixmapBits && pixmapMask && pixmapAll)
93         {
94             /* We have to do some magic here, as cursors are not fully
95              * compatible between Windows and X11. Under X11, there
96              * are only 3 possible color cursor: black, white and
97              * masked. So we map the 4th Windows color (invert the
98              * bits on the screen) to black. This require some boolean
99              * arithmetic:
100              *
101              *         Windows          |          X11
102              * Xor    And      Result   |   Bits     Mask     Result
103              *  0      0     black      |    0        1     background
104              *  0      1     no change  |    X        0     no change
105              *  1      0     white      |    1        1     foreground
106              *  1      1     inverted   |    0        1     background
107              *
108              * which gives:
109              *  Bits = 'Xor' and not 'And'
110              *  Mask = 'Xor' or not 'And'
111              *
112              * FIXME: apparently some servers do support 'inverted' color.
113              * I don't know if it's correct per the X spec, but maybe
114              * we ought to take advantage of it.  -- AJ
115              */
116             XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
117                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
118             XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
119                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
120             XSetFunction( display, BITMAP_monoGC, GXandReverse );
121             XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
122                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
123             XSetFunction( display, BITMAP_monoGC, GXorReverse );
124             XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
125                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
126             XSetFunction( display, BITMAP_monoGC, GXcopy );
127             fg.red = fg.green = fg.blue = 0xffff;
128             bg.red = bg.green = bg.blue = 0x0000;
129             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
130                                 &fg, &bg, ptr->ptHotSpot.x, ptr->ptHotSpot.y );
131         }
132
133         /* Now free everything */
134
135         if (pixmapAll) XFreePixmap( display, pixmapAll );
136         if (pixmapBits) XFreePixmap( display, pixmapBits );
137         if (pixmapMask) XFreePixmap( display, pixmapMask );
138     }
139
140     if (cursor == None) return FALSE;
141     if (X11DRV_MOUSE_XCursor != None) XFreeCursor( display, X11DRV_MOUSE_XCursor );
142     X11DRV_MOUSE_XCursor = cursor;
143
144     if (X11DRV_GetXRootWindow() != DefaultRootWindow(display) || !WIN_GetDesktop())
145     {
146         /* Set the cursor on the desktop window */
147         XDefineCursor( display, X11DRV_GetXRootWindow(), cursor );
148     }
149     else
150     {
151         /* FIXME: this won't work correctly with native USER !*/
152
153         /* Set the same cursor for all top-level windows */
154         HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
155         while(hwnd)
156         {
157             WND *tmpWnd = WIN_FindWndPtr(hwnd);
158             Window win = X11DRV_WND_FindXWindow(tmpWnd );
159             if (win && win!=DefaultRootWindow(display))
160                 XDefineCursor( display, win, cursor );
161             hwnd = GetWindow( hwnd, GW_HWNDNEXT );
162             WIN_ReleaseWndPtr(tmpWnd);
163         }
164     }
165     WIN_ReleaseDesktop();
166     return TRUE;
167 }
168
169 /***********************************************************************
170  *              X11DRV_MOUSE_SetCursor
171  */
172 void X11DRV_MOUSE_SetCursor( CURSORICONINFO *lpCursor )
173 {
174     EnterCriticalSection( &X11DRV_CritSection );
175     CALL_LARGE_STACK( X11DRV_MOUSE_DoSetCursor, lpCursor );
176     LeaveCriticalSection( &X11DRV_CritSection );
177 }
178
179 /***********************************************************************
180  *              X11DRV_MOUSE_MoveCursor
181  */
182 void X11DRV_MOUSE_MoveCursor(WORD wAbsX, WORD wAbsY)
183 {
184   /* 
185    * We do not want the to create MotionNotify events here, 
186    * otherwise we will get an endless recursion:
187    * XMotionEvent -> MOUSEEVENTF_MOVE -> mouse_event -> DisplayMoveCursor
188    * -> XWarpPointer -> XMotionEvent -> ...
189    *
190    * Unfortunately, the XWarpPointer call does create a MotionNotify
191    * event. So, we use a hack: before MOUSE_SendEvent calls the mouse event
192    * procedure, it sets a global flag. If this flag is set, we skip the
193    * XWarpPointer call.  If we are *not* called from within MOUSE_SendEvent,
194    * we will call XWarpPointer, which will create a MotionNotify event.
195    * Strictly speaking, this is also wrong, but that should normally not
196    * have any negative effects ...
197    *
198    * But first of all, we check whether we already are at the position
199    * are supposed to move to; if so, we don't need to do anything.
200    */
201   
202   Window root, child;
203   int rootX, rootY, winX, winY;
204   unsigned int xstate;
205   
206   if (X11DRV_MOUSE_DisableWarpPointer) return;
207
208   if (!TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
209                         &rootX, &rootY, &winX, &winY, &xstate ))
210     return;
211   
212   if ( winX == wAbsX && winY == wAbsY )
213     return;
214   
215   TRACE( cursor, "(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
216   
217   TSXWarpPointer( display, X11DRV_GetXRootWindow(), X11DRV_GetXRootWindow(), 
218                   0, 0, 0, 0, wAbsX, wAbsY );
219 }
220
221 #endif /* !defined(X_DISPLAY_MISSING) */