Spelling typos.
[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 "windef.h"
12 #include "wine/winuser16.h"
13
14 #include "debugtools.h"
15 #include "mouse.h"
16 #include "win.h"
17 #include "x11drv.h"
18
19 DEFAULT_DEBUG_CHANNEL(cursor);
20
21 /**********************************************************************/
22
23 static LONG X11DRV_MOUSE_WarpPointer = 0;  /* hack; see DISPLAY_MoveCursor */
24 static LPMOUSE_EVENT_PROC DefMouseEventProc = NULL;
25
26 /***********************************************************************
27  *              X11DRV_GetCursor
28  */
29 Cursor X11DRV_GetCursor( Display *display, CURSORICONINFO *ptr )
30 {
31     Pixmap pixmapBits, pixmapMask, pixmapMaskInv, 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, root_window, 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         GC gc;
52
53         if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
54         {
55             WARN("Cursor has more than 1 bpp!\n" );
56             return 0;
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         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 );
79         image->data = NULL;
80         XDestroyImage( image );
81
82         /* Now create the 2 pixmaps for bits and mask */
83
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 );
87
88         /* Make sure everything went OK so far */
89
90         if (pixmapBits && pixmapMask && pixmapMaskInv)
91         {
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:
98              *
99              *         Windows          |          X11
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
105              *
106              * which gives:
107              *  Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
108              *  Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
109              *
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
113              */
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 );
141         }
142
143         /* Now free everything */
144
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 );
150     }
151     return cursor;
152 }
153
154 /* set the cursor of a window; helper for X11DRV_SetCursor */
155 static BOOL CALLBACK set_win_cursor( HWND hwnd, LPARAM cursor )
156 {
157     WND *wndPtr = WIN_FindWndPtr(hwnd);
158     if (wndPtr)
159     {
160         Window win = X11DRV_WND_GetXWindow(wndPtr);
161         if (win) TSXDefineCursor( thread_display(), win, (Cursor)cursor );
162     }
163     WIN_ReleaseWndPtr( wndPtr );
164     return TRUE;
165 }
166
167 /***********************************************************************
168  *              SetCursor (X11DRV.@)
169  */
170 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
171 {
172     Cursor cursor;
173
174     if (root_window != DefaultRootWindow(gdi_display))
175     {
176         /* If in desktop mode, set the cursor on the desktop window */
177
178         wine_tsx11_lock();
179         cursor = X11DRV_GetCursor( gdi_display, lpCursor );
180         if (cursor)
181         {
182             XDefineCursor( gdi_display, root_window, cursor );
183             XFreeCursor( gdi_display, cursor );
184         }
185         wine_tsx11_unlock();
186     }
187     else /* set the same cursor for all top-level windows of the current thread */
188     {
189         Display *display = thread_display();
190
191         wine_tsx11_lock();
192         cursor = X11DRV_GetCursor( display, lpCursor );
193         wine_tsx11_unlock();
194         if (cursor)
195         {
196 /*            EnumThreadWindows( GetCurrentThreadId(), set_win_cursor, (LPARAM)cursor );*/
197             EnumWindows( set_win_cursor, (LPARAM)cursor );
198             TSXFreeCursor( display, cursor );
199         }
200     }
201 }
202
203 /***********************************************************************
204  *              MoveCursor (X11DRV.@)
205  */
206 void X11DRV_MoveCursor(WORD wAbsX, WORD wAbsY)
207 {
208   /* 
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 -> ...
213    *
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 ...
221    *
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.
224    */
225
226     Display *display = thread_display();
227   Window root, child;
228   int rootX, rootY, winX, winY;
229   unsigned int xstate;
230   
231   if (X11DRV_MOUSE_WarpPointer < 0) return;
232
233   if (!TSXQueryPointer( display, root_window, &root, &child,
234                         &rootX, &rootY, &winX, &winY, &xstate ))
235     return;
236   
237   if ( winX == wAbsX && winY == wAbsY )
238     return;
239   
240   TRACE("(%d,%d): moving from (%d,%d)\n", wAbsX, wAbsY, winX, winY );
241   
242   TSXWarpPointer( display, root_window, root_window, 0, 0, 0, 0, wAbsX, wAbsY );
243 }
244
245 /***********************************************************************
246  *              InitMouse (X11DRV.@)
247  */
248 void X11DRV_InitMouse( LPMOUSE_EVENT_PROC proc )
249 {
250     static int init_done;
251
252     DefMouseEventProc = proc;
253
254     if (!init_done)
255     {
256         Window root, child;
257         int root_x, root_y, child_x, child_y;
258         unsigned int KeyState;
259   
260         init_done = 1;
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),
267                                GetTickCount(), 0 );
268     }
269 }
270
271
272 /***********************************************************************
273  *              X11DRV_SendEvent (internal)
274  */
275 void X11DRV_SendEvent( DWORD mouseStatus, DWORD posX, DWORD posY, 
276                              DWORD keyState, DWORD time, HWND hWnd )
277 {
278     int width  = GetSystemMetrics( SM_CXSCREEN );
279     int height = GetSystemMetrics( SM_CYSCREEN );
280     int iWndsLocks;
281     WINE_MOUSEEVENT wme;
282
283     if ( !DefMouseEventProc ) return;
284
285     TRACE("(%04lX,%ld,%ld)\n", mouseStatus, posX, posY );
286
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;
292       }
293     }
294
295     wme.magic    = WINE_MOUSEEVENT_MAGIC;
296     wme.time     = time;
297     wme.hWnd     = hWnd;
298     wme.keyState = keyState;
299     
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 );
307 }