winex11.drv: Fix fbconfig regression.
[wine] / dlls / winex11.drv / mouse.c
1 /*
2  * X11 mouse driver
3  *
4  * Copyright 1998 Ulrich Weigand
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include <X11/Xlib.h>
24 #include <stdarg.h>
25
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wine/winuser16.h"
31
32 #include "win.h"
33 #include "x11drv.h"
34 #include "wine/server.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
38
39 /**********************************************************************/
40
41 #ifndef Button6Mask
42 #define Button6Mask (1<<13)
43 #endif
44 #ifndef Button7Mask
45 #define Button7Mask (1<<14)
46 #endif
47
48 #define NB_BUTTONS   7     /* Windows can handle 5 buttons and the wheel too */
49
50 static const UINT button_down_flags[NB_BUTTONS] =
51 {
52     MOUSEEVENTF_LEFTDOWN,
53     MOUSEEVENTF_MIDDLEDOWN,
54     MOUSEEVENTF_RIGHTDOWN,
55     MOUSEEVENTF_WHEEL,
56     MOUSEEVENTF_WHEEL,
57     MOUSEEVENTF_XDOWN,
58     MOUSEEVENTF_XDOWN
59 };
60
61 static const UINT button_up_flags[NB_BUTTONS] =
62 {
63     MOUSEEVENTF_LEFTUP,
64     MOUSEEVENTF_MIDDLEUP,
65     MOUSEEVENTF_RIGHTUP,
66     0,
67     0,
68     MOUSEEVENTF_XUP,
69     MOUSEEVENTF_XUP
70 };
71
72 POINT cursor_pos;
73
74 /***********************************************************************
75  *              get_coords
76  *
77  * get the coordinates of a mouse event
78  */
79 static inline void get_coords( HWND hwnd, int x, int y, POINT *pt )
80 {
81     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
82
83     if (!data) return;
84
85     pt->x = x + data->whole_rect.left;
86     pt->y = y + data->whole_rect.top;
87 }
88
89
90 /***********************************************************************
91  *              update_button_state
92  *
93  * Update the button state with what X provides us
94  */
95 static inline void update_button_state( unsigned int state )
96 {
97     key_state_table[VK_LBUTTON] = (state & Button1Mask ? 0x80 : 0);
98     key_state_table[VK_MBUTTON] = (state & Button2Mask ? 0x80 : 0);
99     key_state_table[VK_RBUTTON] = (state & Button3Mask ? 0x80 : 0);
100     key_state_table[VK_XBUTTON1]= (state & Button6Mask ? 0x80 : 0);
101     key_state_table[VK_XBUTTON2]= (state & Button7Mask ? 0x80 : 0);
102 }
103
104
105 /***********************************************************************
106  *              update_key_state
107  *
108  * Update the key state with what X provides us
109  */
110 static inline void update_key_state( unsigned int state )
111 {
112     key_state_table[VK_SHIFT]   = (state & ShiftMask   ? 0x80 : 0);
113     key_state_table[VK_CONTROL] = (state & ControlMask ? 0x80 : 0);
114 }
115
116
117 /***********************************************************************
118  *              update_mouse_state
119  *
120  * Update the various window states on a mouse event.
121  */
122 static void update_mouse_state( HWND hwnd, Window window, int x, int y, unsigned int state, POINT *pt )
123 {
124     struct x11drv_thread_data *data = x11drv_thread_data();
125
126     get_coords( hwnd, x, y, pt );
127     update_key_state( state );
128
129     /* update the cursor */
130
131     if (data->cursor_window != window)
132     {
133         data->cursor_window = window;
134         wine_tsx11_lock();
135         if (data->cursor) XDefineCursor( data->display, window, data->cursor );
136         wine_tsx11_unlock();
137     }
138
139     /* update the wine server Z-order */
140
141     if (window != data->grab_window &&
142         /* ignore event if a button is pressed, since the mouse is then grabbed too */
143         !(state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask|Button6Mask|Button7Mask)))
144     {
145         SERVER_START_REQ( update_window_zorder )
146         {
147             req->window      = hwnd;
148             req->rect.left   = pt->x;
149             req->rect.top    = pt->y;
150             req->rect.right  = pt->x + 1;
151             req->rect.bottom = pt->y + 1;
152             wine_server_call( req );
153         }
154         SERVER_END_REQ;
155     }
156 }
157
158
159 /***********************************************************************
160  *           get_key_state
161  */
162 static WORD get_key_state(void)
163 {
164     WORD ret = 0;
165
166     if (GetSystemMetrics( SM_SWAPBUTTON ))
167     {
168         if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_LBUTTON;
169         if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_RBUTTON;
170     }
171     else
172     {
173         if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_LBUTTON;
174         if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_RBUTTON;
175     }
176     if (key_state_table[VK_MBUTTON] & 0x80)  ret |= MK_MBUTTON;
177     if (key_state_table[VK_SHIFT] & 0x80)    ret |= MK_SHIFT;
178     if (key_state_table[VK_CONTROL] & 0x80)  ret |= MK_CONTROL;
179     if (key_state_table[VK_XBUTTON1] & 0x80) ret |= MK_XBUTTON1;
180     if (key_state_table[VK_XBUTTON2] & 0x80) ret |= MK_XBUTTON2;
181     return ret;
182 }
183
184
185 /***********************************************************************
186  *           queue_raw_mouse_message
187  */
188 static void queue_raw_mouse_message( UINT message, HWND hwnd, DWORD x, DWORD y,
189                                      DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
190 {
191     MSLLHOOKSTRUCT hook;
192
193     hook.pt.x        = x;
194     hook.pt.y        = y;
195     hook.mouseData   = MAKELONG( 0, data );
196     hook.flags       = injected_flags;
197     hook.time        = time;
198     hook.dwExtraInfo = extra_info;
199
200     if (HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE )) return;
201
202     SERVER_START_REQ( send_hardware_message )
203     {
204         req->id       = (injected_flags & LLMHF_INJECTED) ? 0 : GetCurrentThreadId();
205         req->win      = hwnd;
206         req->msg      = message;
207         req->wparam   = MAKEWPARAM( get_key_state(), data );
208         req->lparam   = 0;
209         req->x        = x;
210         req->y        = y;
211         req->time     = time;
212         req->info     = extra_info;
213         wine_server_call( req );
214     }
215     SERVER_END_REQ;
216
217 }
218
219
220 /***********************************************************************
221  *              X11DRV_send_mouse_input
222  */
223 void X11DRV_send_mouse_input( HWND hwnd, DWORD flags, DWORD x, DWORD y,
224                               DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
225 {
226     POINT pt;
227
228     if (flags & MOUSEEVENTF_ABSOLUTE)
229     {
230         if (injected_flags & LLMHF_INJECTED)
231         {
232             pt.x = (x * screen_width) >> 16;
233             pt.y = (y * screen_height) >> 16;
234         }
235         else
236         {
237             pt.x = x;
238             pt.y = y;
239         }
240         wine_tsx11_lock();
241         cursor_pos = pt;
242         wine_tsx11_unlock();
243     }
244     else if (flags & MOUSEEVENTF_MOVE)
245     {
246         int accel[3], xMult = 1, yMult = 1;
247
248         /* dx and dy can be negative numbers for relative movements */
249         SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
250
251         if (abs(x) > accel[0] && accel[2] != 0)
252         {
253             xMult = 2;
254             if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
255         }
256         if (abs(y) > accel[0] && accel[2] != 0)
257         {
258             yMult = 2;
259             if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
260         }
261
262         wine_tsx11_lock();
263         pt.x = cursor_pos.x + (long)x * xMult;
264         pt.y = cursor_pos.y + (long)y * yMult;
265
266         /* Clip to the current screen size */
267         if (pt.x < 0) pt.x = 0;
268         else if (pt.x >= screen_width) pt.x = screen_width - 1;
269         if (pt.y < 0) pt.y = 0;
270         else if (pt.y >= screen_height) pt.y = screen_height - 1;
271         cursor_pos = pt;
272         wine_tsx11_unlock();
273     }
274     else
275     {
276         wine_tsx11_lock();
277         pt = cursor_pos;
278         wine_tsx11_unlock();
279     }
280
281     if (flags & MOUSEEVENTF_MOVE)
282     {
283         queue_raw_mouse_message( WM_MOUSEMOVE, hwnd, pt.x, pt.y, data, time,
284                                  extra_info, injected_flags );
285         if ((injected_flags & LLMHF_INJECTED) &&
286             ((flags & MOUSEEVENTF_ABSOLUTE) || x || y))  /* we have to actually move the cursor */
287         {
288             TRACE( "warping to (%ld,%ld)\n", pt.x, pt.y );
289             wine_tsx11_lock();
290             XWarpPointer( thread_display(), root_window, root_window, 0, 0, 0, 0, pt.x, pt.y );
291             wine_tsx11_unlock();
292         }
293     }
294     if (flags & MOUSEEVENTF_LEFTDOWN)
295     {
296         key_state_table[VK_LBUTTON] |= 0xc0;
297         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
298                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
299     }
300     if (flags & MOUSEEVENTF_LEFTUP)
301     {
302         key_state_table[VK_LBUTTON] &= ~0x80;
303         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
304                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
305     }
306     if (flags & MOUSEEVENTF_RIGHTDOWN)
307     {
308         key_state_table[VK_RBUTTON] |= 0xc0;
309         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
310                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
311     }
312     if (flags & MOUSEEVENTF_RIGHTUP)
313     {
314         key_state_table[VK_RBUTTON] &= ~0x80;
315         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
316                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
317     }
318     if (flags & MOUSEEVENTF_MIDDLEDOWN)
319     {
320         key_state_table[VK_MBUTTON] |= 0xc0;
321         queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
322                                  extra_info, injected_flags );
323     }
324     if (flags & MOUSEEVENTF_MIDDLEUP)
325     {
326         key_state_table[VK_MBUTTON] &= ~0x80;
327         queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
328                                  extra_info, injected_flags );
329     }
330     if (flags & MOUSEEVENTF_WHEEL)
331     {
332         queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
333                                  extra_info, injected_flags );
334     }
335     if (flags & MOUSEEVENTF_XDOWN)
336     {
337         key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
338         queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
339                                  extra_info, injected_flags );
340     }
341     if (flags & MOUSEEVENTF_XUP)
342     {
343         key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
344         queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
345                                  extra_info, injected_flags );
346     }
347 }
348
349
350 /***********************************************************************
351  *              create_cursor
352  *
353  * Create an X cursor from a Windows one.
354  */
355 static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
356 {
357     Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
358     XColor fg, bg;
359     Cursor cursor = None;
360
361     if (!ptr)  /* Create an empty cursor */
362     {
363         static const char data[] = { 0 };
364
365         bg.red = bg.green = bg.blue = 0x0000;
366         pixmapBits = XCreateBitmapFromData( display, root_window, data, 1, 1 );
367         if (pixmapBits)
368         {
369             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
370                                           &bg, &bg, 0, 0 );
371             XFreePixmap( display, pixmapBits );
372         }
373     }
374     else  /* Create the X cursor from the bits */
375     {
376         XImage *image;
377         GC gc;
378
379         TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
380             ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
381             ptr->nWidthBytes);
382         /* Create a pixmap and transfer all the bits to it */
383
384         /* NOTE: Following hack works, but only because XFree depth
385          *       1 images really use 1 bit/pixel (and so the same layout
386          *       as the Windows cursor data). Perhaps use a more generic
387          *       algorithm here.
388          */
389         /* This pixmap will be written with two bitmaps. The first is
390          *  the mask and the second is the image.
391          */
392         if (!(pixmapAll = XCreatePixmap( display, root_window,
393                   ptr->nWidth, ptr->nHeight * 2, 1 )))
394             return 0;
395         if (!(image = XCreateImage( display, visual,
396                 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
397                 ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
398         {
399             XFreePixmap( display, pixmapAll );
400             return 0;
401         }
402         gc = XCreateGC( display, pixmapAll, 0, NULL );
403         XSetGraphicsExposures( display, gc, False );
404         image->byte_order = MSBFirst;
405         image->bitmap_bit_order = MSBFirst;
406         image->bitmap_unit = 16;
407         _XInitImageFuncPtrs(image);
408         if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
409         {
410             /* A plain old white on black cursor. */
411             fg.red = fg.green = fg.blue = 0xffff;
412             bg.red = bg.green = bg.blue = 0x0000;
413             XPutImage( display, pixmapAll, gc, image,
414                 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
415         }
416         else
417         {
418             int     rbits, gbits, bbits, red, green, blue;
419             int     rfg, gfg, bfg, rbg, gbg, bbg;
420             int     rscale, gscale, bscale;
421             int     x, y, xmax, ymax, bitIndex, byteIndex, xorIndex;
422             unsigned char *theMask, *theImage, theChar;
423             int     threshold, fgBits, bgBits, bitShifted;
424             BYTE    pXorBits[128];   /* Up to 32x32 icons */
425
426             switch (ptr->bBitsPerPixel)
427             {
428             case 24:
429                 rbits = 8;
430                 gbits = 8;
431                 bbits = 8;
432                 threshold = 0x40;
433                 break;
434             case 16:
435                 rbits = 5;
436                 gbits = 6;
437                 bbits = 5;
438                 threshold = 0x40;
439                 break;
440             default:
441                 FIXME("Currently no support for cursors with %d bits per pixel\n",
442                   ptr->bBitsPerPixel);
443                 XFreePixmap( display, pixmapAll );
444                 XFreeGC( display, gc );
445                 image->data = NULL;
446                 XDestroyImage( image );
447                 return 0;
448             }
449             /* The location of the mask. */
450             theMask = (unsigned char *)(ptr + 1);
451             /* The mask should still be 1 bit per pixel. The color image
452              * should immediately follow the mask.
453              */
454             theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
455             rfg = gfg = bfg = rbg = gbg = bbg = 0;
456             bitIndex = 0;
457             byteIndex = 0;
458             xorIndex = 0;
459             fgBits = 0;
460             bitShifted = 0x01;
461             xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
462             if (ptr->nWidth > 32) {
463                 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
464                   ptr->nWidth, ptr->nHeight);
465             }
466             ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
467
468             memset(pXorBits, 0, 128);
469             for (y=0; y<ymax; y++)
470             {
471                 for (x=0; x<xmax; x++)
472                 {
473                         red = green = blue = 0;
474                         switch (ptr->bBitsPerPixel)
475                         {
476                         case 24:
477                             theChar = theImage[byteIndex++];
478                             blue = theChar;
479                             theChar = theImage[byteIndex++];
480                             green = theChar;
481                             theChar = theImage[byteIndex++];
482                             red = theChar;
483                             break;
484                         case 16:
485                             theChar = theImage[byteIndex++];
486                             blue = theChar & 0x1F;
487                             green = (theChar & 0xE0) >> 5;
488                             theChar = theImage[byteIndex++];
489                             green |= (theChar & 0x07) << 3;
490                             red = (theChar & 0xF8) >> 3;
491                             break;
492                         }
493
494                     if (red+green+blue > threshold)
495                     {
496                         rfg += red;
497                         gfg += green;
498                         bfg += blue;
499                         fgBits++;
500                         pXorBits[xorIndex] |= bitShifted;
501                     }
502                     else
503                     {
504                         rbg += red;
505                         gbg += green;
506                         bbg += blue;
507                     }
508                     if (x%8 == 7)
509                     {
510                         bitShifted = 0x01;
511                         xorIndex++;
512                     }
513                     else
514                         bitShifted = bitShifted << 1;
515                 }
516             }
517             rscale = 1 << (16 - rbits);
518             gscale = 1 << (16 - gbits);
519             bscale = 1 << (16 - bbits);
520             if (fgBits)
521             {
522                 fg.red   = rfg * rscale / fgBits;
523                 fg.green = gfg * gscale / fgBits;
524                 fg.blue  = bfg * bscale / fgBits;
525             }
526             else fg.red = fg.green = fg.blue = 0;
527             bgBits = xmax * ymax - fgBits;
528             if (bgBits)
529             {
530                 bg.red   = rbg * rscale / bgBits;
531                 bg.green = gbg * gscale / bgBits;
532                 bg.blue  = bbg * bscale / bgBits;
533             }
534             else bg.red = bg.green = bg.blue = 0;
535             pixmapBits = XCreateBitmapFromData( display, root_window, (char *)pXorBits, xmax, ymax );
536             if (!pixmapBits)
537             {
538                 XFreePixmap( display, pixmapAll );
539                 XFreeGC( display, gc );
540                 image->data = NULL;
541                 XDestroyImage( image );
542                 return 0;
543             }
544
545             /* Put the mask. */
546             XPutImage( display, pixmapAll, gc, image,
547                    0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
548             XSetFunction( display, gc, GXcopy );
549             /* Put the image */
550             XCopyArea( display, pixmapBits, pixmapAll, gc,
551                        0, 0, xmax, ymax, 0, ptr->nHeight );
552             XFreePixmap( display, pixmapBits );
553         }
554         image->data = NULL;
555         XDestroyImage( image );
556
557         /* Now create the 2 pixmaps for bits and mask */
558
559         pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
560         pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
561         pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
562
563         /* Make sure everything went OK so far */
564
565         if (pixmapBits && pixmapMask && pixmapMaskInv)
566         {
567             POINT hotspot;
568
569             /* We have to do some magic here, as cursors are not fully
570              * compatible between Windows and X11. Under X11, there
571              * are only 3 possible color cursor: black, white and
572              * masked. So we map the 4th Windows color (invert the
573              * bits on the screen) to black and an additional white bit on
574              * an other place (+1,+1). This require some boolean arithmetic:
575              *
576              *         Windows          |          X11
577              * And    Xor      Result   |   Bits     Mask     Result
578              *  0      0     black      |    0        1     background
579              *  0      1     white      |    1        1     foreground
580              *  1      0     no change  |    X        0     no change
581              *  1      1     inverted   |    0        1     background
582              *
583              * which gives:
584              *  Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
585              *  Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
586              *
587              * FIXME: apparently some servers do support 'inverted' color.
588              * I don't know if it's correct per the X spec, but maybe
589              * we ought to take advantage of it.  -- AJ
590              */
591             XSetFunction( display, gc, GXcopy );
592             XCopyArea( display, pixmapAll, pixmapBits, gc,
593                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
594             XCopyArea( display, pixmapAll, pixmapMask, gc,
595                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
596             XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
597                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
598             XSetFunction( display, gc, GXand );
599             XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
600                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
601             XSetFunction( display, gc, GXandReverse );
602             XCopyArea( display, pixmapAll, pixmapBits, gc,
603                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
604             XSetFunction( display, gc, GXorReverse );
605             XCopyArea( display, pixmapAll, pixmapMask, gc,
606                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
607             /* Additional white */
608             XSetFunction( display, gc, GXor );
609             XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
610                        0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
611             XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
612                        0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
613             XSetFunction( display, gc, GXcopy );
614
615             /* Make sure hotspot is valid */
616             hotspot.x = ptr->ptHotSpot.x;
617             hotspot.y = ptr->ptHotSpot.y;
618             if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
619                 hotspot.y < 0 || hotspot.y >= ptr->nHeight)
620             {
621                 hotspot.x = ptr->nWidth / 2;
622                 hotspot.y = ptr->nHeight / 2;
623             }
624             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
625                                           &fg, &bg, hotspot.x, hotspot.y );
626         }
627
628         /* Now free everything */
629
630         if (pixmapAll) XFreePixmap( display, pixmapAll );
631         if (pixmapBits) XFreePixmap( display, pixmapBits );
632         if (pixmapMask) XFreePixmap( display, pixmapMask );
633         if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
634         XFreeGC( display, gc );
635     }
636     return cursor;
637 }
638
639
640 /***********************************************************************
641  *              SetCursor (X11DRV.@)
642  */
643 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
644 {
645     Cursor cursor;
646
647     if (root_window != DefaultRootWindow(gdi_display))
648     {
649         /* If in desktop mode, set the cursor on the desktop window */
650
651         wine_tsx11_lock();
652         cursor = create_cursor( gdi_display, lpCursor );
653         if (cursor)
654         {
655             XDefineCursor( gdi_display, root_window, cursor );
656             /* Make the change take effect immediately */
657             XFlush(gdi_display);
658             XFreeCursor( gdi_display, cursor );
659         }
660         wine_tsx11_unlock();
661     }
662     else /* set the same cursor for all top-level windows of the current thread */
663     {
664         struct x11drv_thread_data *data = x11drv_thread_data();
665
666         wine_tsx11_lock();
667         cursor = create_cursor( data->display, lpCursor );
668         if (cursor)
669         {
670             if (data->cursor) XFreeCursor( data->display, data->cursor );
671             data->cursor = cursor;
672             if (data->cursor_window)
673             {
674                 XDefineCursor( data->display, data->cursor_window, cursor );
675                 /* Make the change take effect immediately */
676                 XFlush( data->display );
677             }
678         }
679         wine_tsx11_unlock();
680     }
681 }
682
683 /***********************************************************************
684  *              SetCursorPos (X11DRV.@)
685  */
686 BOOL X11DRV_SetCursorPos( INT x, INT y )
687 {
688     Display *display = thread_display();
689
690     TRACE( "warping to (%d,%d)\n", x, y );
691
692     wine_tsx11_lock();
693     XWarpPointer( display, root_window, root_window, 0, 0, 0, 0, x, y );
694     XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
695     cursor_pos.x = x;
696     cursor_pos.y = y;
697     wine_tsx11_unlock();
698     return TRUE;
699 }
700
701 /***********************************************************************
702  *              GetCursorPos (X11DRV.@)
703  */
704 BOOL X11DRV_GetCursorPos(LPPOINT pos)
705 {
706     Display *display = thread_display();
707     Window root, child;
708     int rootX, rootY, winX, winY;
709     unsigned int xstate;
710
711     wine_tsx11_lock();
712     if (XQueryPointer( display, root_window, &root, &child,
713                        &rootX, &rootY, &winX, &winY, &xstate ))
714     {
715         update_key_state( xstate );
716         update_button_state( xstate );
717         TRACE("pointer at (%d,%d)\n", winX, winY );
718         cursor_pos.x = winX;
719         cursor_pos.y = winY;
720     }
721     *pos = cursor_pos;
722     wine_tsx11_unlock();
723     return TRUE;
724 }
725
726 /***********************************************************************
727  *           X11DRV_ButtonPress
728  */
729 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
730 {
731     XButtonEvent *event = &xev->xbutton;
732     int buttonNum = event->button - 1;
733     WORD wData = 0;
734     POINT pt;
735
736     if (buttonNum >= NB_BUTTONS) return;
737     if (!hwnd) return;
738
739     switch (buttonNum)
740     {
741     case 3:
742         wData = WHEEL_DELTA;
743         break;
744     case 4:
745         wData = -WHEEL_DELTA;
746         break;
747     case 5:
748         wData = XBUTTON1;
749         break;
750     case 6:
751         wData = XBUTTON2;
752         break;
753     }
754
755     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
756
757     X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE,
758                              pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
759 }
760
761
762 /***********************************************************************
763  *           X11DRV_ButtonRelease
764  */
765 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
766 {
767     XButtonEvent *event = &xev->xbutton;
768     int buttonNum = event->button - 1;
769     WORD wData = 0;
770     POINT pt;
771
772     if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
773     if (!hwnd) return;
774
775     switch (buttonNum)
776     {
777     case 5:
778         wData = XBUTTON1;
779         break;
780     case 6:
781         wData = XBUTTON2;
782         break;
783     }
784
785     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
786
787     X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE,
788                              pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
789 }
790
791
792 /***********************************************************************
793  *           X11DRV_MotionNotify
794  */
795 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
796 {
797     XMotionEvent *event = &xev->xmotion;
798     POINT pt;
799
800     TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
801
802     if (!hwnd) return;
803
804     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
805
806     X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
807                              pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
808 }
809
810
811 /***********************************************************************
812  *           X11DRV_EnterNotify
813  */
814 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
815 {
816     XCrossingEvent *event = &xev->xcrossing;
817     POINT pt;
818
819     TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
820
821     if (!hwnd) return;
822     if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
823
824     /* simulate a mouse motion event */
825     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
826
827     X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
828                              pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
829 }