msvcp90/tests: Added char_traits equal tests.
[wine] / dlls / winex11.drv / mouse.c
1 /*
2  * X11 mouse driver
3  *
4  * Copyright 1998 Ulrich Weigand
5  * Copyright 2007 Henri Verbeet
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <X11/Xlib.h>
26 #include <stdarg.h>
27
28 #ifdef SONAME_LIBXCURSOR
29 # include <X11/Xcursor/Xcursor.h>
30 static void *xcursor_handle;
31 # define MAKE_FUNCPTR(f) static typeof(f) * p##f
32 MAKE_FUNCPTR(XcursorImageCreate);
33 MAKE_FUNCPTR(XcursorImageDestroy);
34 MAKE_FUNCPTR(XcursorImageLoadCursor);
35 # undef MAKE_FUNCPTR
36 #endif /* SONAME_LIBXCURSOR */
37
38 #define NONAMELESSUNION
39 #define NONAMELESSSTRUCT
40 #include "windef.h"
41 #include "winbase.h"
42
43 #include "x11drv.h"
44 #include "wine/server.h"
45 #include "wine/library.h"
46 #include "wine/debug.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
49
50 /**********************************************************************/
51
52 #ifndef Button6Mask
53 #define Button6Mask (1<<13)
54 #endif
55 #ifndef Button7Mask
56 #define Button7Mask (1<<14)
57 #endif
58
59 #define NB_BUTTONS   9     /* Windows can handle 5 buttons and the wheel too */
60
61 static const UINT button_down_flags[NB_BUTTONS] =
62 {
63     MOUSEEVENTF_LEFTDOWN,
64     MOUSEEVENTF_MIDDLEDOWN,
65     MOUSEEVENTF_RIGHTDOWN,
66     MOUSEEVENTF_WHEEL,
67     MOUSEEVENTF_WHEEL,
68     MOUSEEVENTF_XDOWN,  /* FIXME: horizontal wheel */
69     MOUSEEVENTF_XDOWN,
70     MOUSEEVENTF_XDOWN,
71     MOUSEEVENTF_XDOWN
72 };
73
74 static const UINT button_up_flags[NB_BUTTONS] =
75 {
76     MOUSEEVENTF_LEFTUP,
77     MOUSEEVENTF_MIDDLEUP,
78     MOUSEEVENTF_RIGHTUP,
79     0,
80     0,
81     MOUSEEVENTF_XUP,
82     MOUSEEVENTF_XUP,
83     MOUSEEVENTF_XUP,
84     MOUSEEVENTF_XUP
85 };
86
87 POINT cursor_pos;
88 static HWND cursor_window;
89 static DWORD last_time_modified;
90 static RECT cursor_clip; /* Cursor clipping rect */
91 static XContext cursor_context;
92 static Cursor create_cursor( HANDLE handle );
93
94 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y );
95
96
97 /***********************************************************************
98  *              X11DRV_Xcursor_Init
99  *
100  * Load the Xcursor library for use.
101  */
102 void X11DRV_Xcursor_Init(void)
103 {
104 #ifdef SONAME_LIBXCURSOR
105     xcursor_handle = wine_dlopen(SONAME_LIBXCURSOR, RTLD_NOW, NULL, 0);
106     if (!xcursor_handle)  /* wine_dlopen failed. */
107     {
108         WARN("Xcursor failed to load.  Using fallback code.\n");
109         return;
110     }
111 #define LOAD_FUNCPTR(f) \
112         p##f = wine_dlsym(xcursor_handle, #f, NULL, 0)
113
114     LOAD_FUNCPTR(XcursorImageCreate);
115     LOAD_FUNCPTR(XcursorImageDestroy);
116     LOAD_FUNCPTR(XcursorImageLoadCursor);
117 #undef LOAD_FUNCPTR
118 #endif /* SONAME_LIBXCURSOR */
119 }
120
121
122 /***********************************************************************
123  *              get_coords
124  *
125  * get the coordinates of a mouse event
126  */
127 static inline void get_coords( HWND hwnd, Window window, int x, int y, POINT *pt )
128 {
129     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
130
131     if (!data) return;
132
133     if (window == data->client_window)
134     {
135         pt->x = x + data->client_rect.left;
136         pt->y = y + data->client_rect.top;
137     }
138     else
139     {
140         pt->x = x + data->whole_rect.left;
141         pt->y = y + data->whole_rect.top;
142     }
143 }
144
145 /***********************************************************************
146  *              clip_point_to_rect
147  *
148  * Clip point to the provided rectangle
149  */
150 static inline void clip_point_to_rect( LPCRECT rect, LPPOINT pt )
151 {
152     if      (pt->x <  rect->left)   pt->x = rect->left;
153     else if (pt->x >= rect->right)  pt->x = rect->right - 1;
154     if      (pt->y <  rect->top)    pt->y = rect->top;
155     else if (pt->y >= rect->bottom) pt->y = rect->bottom - 1;
156 }
157
158 /***********************************************************************
159  *              update_button_state
160  *
161  * Update the button state with what X provides us
162  */
163 static inline void update_button_state( unsigned int state )
164 {
165     key_state_table[VK_LBUTTON] = (state & Button1Mask ? 0x80 : 0);
166     key_state_table[VK_MBUTTON] = (state & Button2Mask ? 0x80 : 0);
167     key_state_table[VK_RBUTTON] = (state & Button3Mask ? 0x80 : 0);
168     /* X-buttons are not reported from XQueryPointer */
169 }
170
171 /***********************************************************************
172  *              get_empty_cursor
173  */
174 static Cursor get_empty_cursor(void)
175 {
176     static Cursor cursor;
177     static const char data[] = { 0 };
178
179     wine_tsx11_lock();
180     if (!cursor)
181     {
182         XColor bg;
183         Pixmap pixmap;
184
185         bg.red = bg.green = bg.blue = 0x0000;
186         pixmap = XCreateBitmapFromData( gdi_display, root_window, data, 1, 1 );
187         if (pixmap)
188         {
189             cursor = XCreatePixmapCursor( gdi_display, pixmap, pixmap, &bg, &bg, 0, 0 );
190             XFreePixmap( gdi_display, pixmap );
191         }
192     }
193     wine_tsx11_unlock();
194     return cursor;
195 }
196
197 /***********************************************************************
198  *              set_window_cursor
199  */
200 void set_window_cursor( HWND hwnd, HCURSOR handle )
201 {
202     struct x11drv_win_data *data;
203     Cursor cursor, prev;
204
205     if (!(data = X11DRV_get_win_data( hwnd ))) return;
206
207     wine_tsx11_lock();
208     if (!handle) cursor = get_empty_cursor();
209     else if (!cursor_context || XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&cursor ))
210     {
211         /* try to create it */
212         wine_tsx11_unlock();
213         if (!(cursor = create_cursor( handle ))) return;
214
215         wine_tsx11_lock();
216         if (!cursor_context) cursor_context = XUniqueContext();
217         if (!XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&prev ))
218         {
219             /* someone else was here first */
220             XFreeCursor( gdi_display, cursor );
221             cursor = prev;
222         }
223         else
224         {
225             XSaveContext( gdi_display, (XID)handle, cursor_context, (char *)cursor );
226             TRACE( "cursor %p created %lx\n", handle, cursor );
227         }
228     }
229
230     XDefineCursor( gdi_display, data->whole_window, cursor );
231     /* make the change take effect immediately */
232     XFlush( gdi_display );
233     data->cursor = handle;
234     wine_tsx11_unlock();
235 }
236
237 /***********************************************************************
238  *              update_mouse_state
239  *
240  * Update the various window states on a mouse event.
241  */
242 static void update_mouse_state( HWND hwnd, Window window, int x, int y, unsigned int state, POINT *pt )
243 {
244     struct x11drv_thread_data *data = x11drv_thread_data();
245
246     get_coords( hwnd, window, x, y, pt );
247
248     cursor_window = hwnd;
249
250     /* update the wine server Z-order */
251
252     if (window != data->grab_window &&
253         /* ignore event if a button is pressed, since the mouse is then grabbed too */
254         !(state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask|Button6Mask|Button7Mask)))
255     {
256         SERVER_START_REQ( update_window_zorder )
257         {
258             req->window      = wine_server_user_handle( hwnd );
259             req->rect.left   = pt->x;
260             req->rect.top    = pt->y;
261             req->rect.right  = pt->x + 1;
262             req->rect.bottom = pt->y + 1;
263             wine_server_call( req );
264         }
265         SERVER_END_REQ;
266     }
267 }
268
269
270 /***********************************************************************
271  *           get_key_state
272  */
273 static WORD get_key_state(void)
274 {
275     WORD ret = 0;
276
277     if (GetSystemMetrics( SM_SWAPBUTTON ))
278     {
279         if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_LBUTTON;
280         if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_RBUTTON;
281     }
282     else
283     {
284         if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_LBUTTON;
285         if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_RBUTTON;
286     }
287     if (key_state_table[VK_MBUTTON] & 0x80)  ret |= MK_MBUTTON;
288     if (key_state_table[VK_SHIFT] & 0x80)    ret |= MK_SHIFT;
289     if (key_state_table[VK_CONTROL] & 0x80)  ret |= MK_CONTROL;
290     if (key_state_table[VK_XBUTTON1] & 0x80) ret |= MK_XBUTTON1;
291     if (key_state_table[VK_XBUTTON2] & 0x80) ret |= MK_XBUTTON2;
292     return ret;
293 }
294
295
296 /***********************************************************************
297  *           queue_raw_mouse_message
298  */
299 static void queue_raw_mouse_message( UINT message, HWND hwnd, DWORD x, DWORD y,
300                                      DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
301 {
302     MSLLHOOKSTRUCT hook;
303     HCURSOR cursor;
304
305     hook.pt.x        = x;
306     hook.pt.y        = y;
307     hook.mouseData   = MAKELONG( 0, data );
308     hook.flags       = injected_flags;
309     hook.time        = time;
310     hook.dwExtraInfo = extra_info;
311
312     last_time_modified = GetTickCount();
313     if (HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE ))
314         message = 0;  /* ignore it */
315
316     SERVER_START_REQ( send_hardware_message )
317     {
318         req->id       = (injected_flags & LLMHF_INJECTED) ? 0 : GetCurrentThreadId();
319         req->win      = wine_server_user_handle( hwnd );
320         req->msg      = message;
321         req->wparam   = MAKEWPARAM( get_key_state(), data );
322         req->lparam   = 0;
323         req->x        = x;
324         req->y        = y;
325         req->time     = time;
326         req->info     = extra_info;
327         wine_server_call( req );
328         cursor = (reply->count >= 0) ? wine_server_ptr_handle(reply->cursor) : 0;
329     }
330     SERVER_END_REQ;
331
332     if (hwnd)
333     {
334         struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
335         if (data && cursor != data->cursor) set_window_cursor( hwnd, cursor );
336     }
337 }
338
339
340 /***********************************************************************
341  *              X11DRV_send_mouse_input
342  */
343 void X11DRV_send_mouse_input( HWND hwnd, DWORD flags, DWORD x, DWORD y,
344                               DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
345 {
346     POINT pt;
347
348     if (flags & MOUSEEVENTF_MOVE && flags & MOUSEEVENTF_ABSOLUTE)
349     {
350         if (injected_flags & LLMHF_INJECTED)
351         {
352             pt.x = (x * screen_width) >> 16;
353             pt.y = (y * screen_height) >> 16;
354         }
355         else
356         {
357             pt.x = x;
358             pt.y = y;
359             wine_tsx11_lock();
360             if (cursor_pos.x == x && cursor_pos.y == y &&
361                 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
362                 flags &= ~MOUSEEVENTF_MOVE;
363             wine_tsx11_unlock();
364         }
365     }
366     else if (flags & MOUSEEVENTF_MOVE)
367     {
368         int accel[3], xMult = 1, yMult = 1;
369
370         /* dx and dy can be negative numbers for relative movements */
371         SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
372
373         if (abs(x) > accel[0] && accel[2] != 0)
374         {
375             xMult = 2;
376             if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
377         }
378         if (abs(y) > accel[0] && accel[2] != 0)
379         {
380             yMult = 2;
381             if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
382         }
383
384         wine_tsx11_lock();
385         pt.x = cursor_pos.x + (long)x * xMult;
386         pt.y = cursor_pos.y + (long)y * yMult;
387         wine_tsx11_unlock();
388     }
389     else
390     {
391         wine_tsx11_lock();
392         pt = cursor_pos;
393         wine_tsx11_unlock();
394     }
395
396     if (flags & MOUSEEVENTF_MOVE)
397     {
398         queue_raw_mouse_message( WM_MOUSEMOVE, hwnd, pt.x, pt.y, data, time,
399                                  extra_info, injected_flags );
400         if ((injected_flags & LLMHF_INJECTED) &&
401             ((flags & MOUSEEVENTF_ABSOLUTE) || x || y))  /* we have to actually move the cursor */
402         {
403             X11DRV_SetCursorPos( pt.x, pt.y );
404         }
405         else
406         {
407             wine_tsx11_lock();
408             clip_point_to_rect( &cursor_clip, &pt);
409             cursor_pos = pt;
410             wine_tsx11_unlock();
411         }
412     }
413     if (flags & MOUSEEVENTF_LEFTDOWN)
414     {
415         key_state_table[VK_LBUTTON] |= 0xc0;
416         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
417                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
418     }
419     if (flags & MOUSEEVENTF_LEFTUP)
420     {
421         key_state_table[VK_LBUTTON] &= ~0x80;
422         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
423                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
424     }
425     if (flags & MOUSEEVENTF_RIGHTDOWN)
426     {
427         key_state_table[VK_RBUTTON] |= 0xc0;
428         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
429                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
430     }
431     if (flags & MOUSEEVENTF_RIGHTUP)
432     {
433         key_state_table[VK_RBUTTON] &= ~0x80;
434         queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
435                                  hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
436     }
437     if (flags & MOUSEEVENTF_MIDDLEDOWN)
438     {
439         key_state_table[VK_MBUTTON] |= 0xc0;
440         queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
441                                  extra_info, injected_flags );
442     }
443     if (flags & MOUSEEVENTF_MIDDLEUP)
444     {
445         key_state_table[VK_MBUTTON] &= ~0x80;
446         queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
447                                  extra_info, injected_flags );
448     }
449     if (flags & MOUSEEVENTF_WHEEL)
450     {
451         queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
452                                  extra_info, injected_flags );
453     }
454     if (flags & MOUSEEVENTF_XDOWN)
455     {
456         key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
457         queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
458                                  extra_info, injected_flags );
459     }
460     if (flags & MOUSEEVENTF_XUP)
461     {
462         key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
463         queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
464                                  extra_info, injected_flags );
465     }
466 }
467
468 #ifdef SONAME_LIBXCURSOR
469
470 /***********************************************************************
471  *              create_xcursor_cursor
472  *
473  * Use Xcursor to create an X cursor from a Windows one.
474  */
475 static Cursor create_xcursor_cursor( HDC hdc, ICONINFO *icon, int width, int height )
476 {
477     int x, y, i, has_alpha;
478     BITMAPINFO *info;
479     Cursor cursor;
480     XcursorImage *image;
481     XcursorPixel *ptr;
482
483     if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return 0;
484
485     wine_tsx11_lock();
486     image = pXcursorImageCreate( width, height );
487     wine_tsx11_unlock();
488     if (!image)
489     {
490         HeapFree( GetProcessHeap(), 0, info );
491         return 0;
492     }
493
494     image->xhot = icon->xHotspot;
495     image->yhot = icon->yHotspot;
496     image->delay = 0;
497
498     info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
499     info->bmiHeader.biWidth = width;
500     info->bmiHeader.biHeight = -height;
501     info->bmiHeader.biPlanes = 1;
502     info->bmiHeader.biBitCount = 32;
503     info->bmiHeader.biCompression = BI_RGB;
504     info->bmiHeader.biSizeImage = width * height * 4;
505     info->bmiHeader.biXPelsPerMeter = 0;
506     info->bmiHeader.biYPelsPerMeter = 0;
507     info->bmiHeader.biClrUsed = 0;
508     info->bmiHeader.biClrImportant = 0;
509     GetDIBits( hdc, icon->hbmColor, 0, height, image->pixels, info, DIB_RGB_COLORS );
510
511     for (i = 0, ptr = image->pixels; i < width * height; i++, ptr++)
512         if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
513
514     if (!has_alpha)
515     {
516         unsigned char *mask_bits;
517         unsigned int width_bytes = (width + 31) / 32 * 4;
518
519         /* generate alpha channel from the mask */
520         info->bmiHeader.biBitCount = 1;
521         info->bmiHeader.biSizeImage = width_bytes * height;
522         if ((mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage )))
523         {
524             GetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS );
525             for (y = 0, ptr = image->pixels; y < height; y++)
526                 for (x = 0; x < width; x++, ptr++)
527                     if (!((mask_bits[y * width_bytes + x / 8] << (x % 8)) & 0x80))
528                         *ptr |= 0xff000000;
529             HeapFree( GetProcessHeap(), 0, mask_bits );
530         }
531     }
532     HeapFree( GetProcessHeap(), 0, info );
533
534     wine_tsx11_lock();
535     cursor = pXcursorImageLoadCursor( gdi_display, image );
536     pXcursorImageDestroy( image );
537     wine_tsx11_unlock();
538     return cursor;
539 }
540
541 #endif /* SONAME_LIBXCURSOR */
542
543
544 /***********************************************************************
545  *              create_cursor_from_bitmaps
546  *
547  * Create an X11 cursor from source bitmaps.
548  */
549 static Cursor create_cursor_from_bitmaps( HBITMAP src_xor, HBITMAP src_and, int width, int height,
550                                           int xor_y, int and_y, XColor *fg, XColor *bg,
551                                           int hotspot_x, int hotspot_y )
552 {
553     HDC src = 0, dst = 0;
554     HBITMAP bits = 0, mask = 0, mask_inv = 0;
555     Cursor cursor = 0;
556
557     if (!(src = CreateCompatibleDC( 0 ))) goto done;
558     if (!(dst = CreateCompatibleDC( 0 ))) goto done;
559
560     if (!(bits = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
561     if (!(mask = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
562     if (!(mask_inv = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
563
564     /* We have to do some magic here, as cursors are not fully
565      * compatible between Windows and X11. Under X11, there are
566      * only 3 possible color cursor: black, white and masked. So
567      * we map the 4th Windows color (invert the bits on the screen)
568      * to black and an additional white bit on an other place
569      * (+1,+1). This require some boolean arithmetic:
570      *
571      *         Windows          |          X11
572      * And    Xor      Result   |   Bits     Mask     Result
573      *  0      0     black      |    0        1     background
574      *  0      1     white      |    1        1     foreground
575      *  1      0     no change  |    X        0     no change
576      *  1      1     inverted   |    0        1     background
577      *
578      * which gives:
579      *  Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
580      *  Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
581      */
582     SelectObject( src, src_and );
583     SelectObject( dst, bits );
584     BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
585     SelectObject( dst, mask );
586     BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
587     SelectObject( dst, mask_inv );
588     BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
589     SelectObject( src, src_xor );
590     BitBlt( dst, 0, 0, width, height, src, 0, xor_y, SRCAND /* src & dst */ );
591     SelectObject( dst, bits );
592     BitBlt( dst, 0, 0, width, height, src, 0, xor_y, SRCERASE /* src & ~dst */ );
593     SelectObject( dst, mask );
594     BitBlt( dst, 0, 0, width, height, src, 0, xor_y, 0xdd0228 /* src | ~dst */ );
595     /* additional white */
596     SelectObject( src, mask_inv );
597     BitBlt( dst, 1, 1, width, height, src, 0, 0, SRCPAINT /* src | dst */);
598     SelectObject( dst, bits );
599     BitBlt( dst, 1, 1, width, height, src, 0, 0, SRCPAINT /* src | dst */ );
600
601     wine_tsx11_lock();
602     cursor = XCreatePixmapCursor( gdi_display, X11DRV_get_pixmap(bits), X11DRV_get_pixmap(mask),
603                                   fg, bg, hotspot_x, hotspot_y );
604     wine_tsx11_unlock();
605
606 done:
607     DeleteDC( src );
608     DeleteDC( dst );
609     DeleteObject( bits );
610     DeleteObject( mask );
611     DeleteObject( mask_inv );
612     return cursor;
613 }
614
615 /***********************************************************************
616  *              create_xlib_cursor
617  *
618  * Create an X cursor from a Windows one.
619  */
620 static Cursor create_xlib_cursor( HDC hdc, ICONINFO *icon, int width, int height )
621 {
622     XColor fg, bg;
623     Cursor cursor = None;
624     HBITMAP xor_bitmap = 0;
625     BITMAPINFO *info;
626     unsigned int *color_bits = NULL, *ptr;
627     unsigned char *mask_bits = NULL, *xor_bits = NULL;
628     int i, x, y, has_alpha = 0;
629     int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
630     unsigned int width_bytes = (width + 31) / 32 * 4;
631
632     if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] ))))
633         return FALSE;
634     info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
635     info->bmiHeader.biWidth = width;
636     info->bmiHeader.biHeight = -height;
637     info->bmiHeader.biPlanes = 1;
638     info->bmiHeader.biBitCount = 1;
639     info->bmiHeader.biCompression = BI_RGB;
640     info->bmiHeader.biSizeImage = width_bytes * height;
641     info->bmiHeader.biXPelsPerMeter = 0;
642     info->bmiHeader.biYPelsPerMeter = 0;
643     info->bmiHeader.biClrUsed = 0;
644     info->bmiHeader.biClrImportant = 0;
645
646     if (!(mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done;
647     if (!GetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS )) goto done;
648
649     info->bmiHeader.biBitCount = 32;
650     info->bmiHeader.biSizeImage = width * height * 4;
651     if (!(color_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done;
652     if (!(xor_bits = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, width_bytes * height ))) goto done;
653     GetDIBits( hdc, icon->hbmColor, 0, height, color_bits, info, DIB_RGB_COLORS );
654
655     /* compute fg/bg color and xor bitmap based on average of the color values */
656
657     if (!(xor_bitmap = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
658     rfg = gfg = bfg = rbg = gbg = bbg = fgBits = 0;
659     for (y = 0, ptr = color_bits; y < height; y++)
660     {
661         for (x = 0; x < width; x++, ptr++)
662         {
663             int red   = (*ptr >> 16) & 0xff;
664             int green = (*ptr >> 8) & 0xff;
665             int blue  = (*ptr >> 0) & 0xff;
666             if (red + green + blue > 0x40)
667             {
668                 rfg += red;
669                 gfg += green;
670                 bfg += blue;
671                 fgBits++;
672                 xor_bits[y * width_bytes + x / 8] |= 0x80 >> (x % 8);
673             }
674             else
675             {
676                 rbg += red;
677                 gbg += green;
678                 bbg += blue;
679             }
680         }
681     }
682     if (fgBits)
683     {
684         fg.red   = rfg * 257 / fgBits;
685         fg.green = gfg * 257 / fgBits;
686         fg.blue  = bfg * 257 / fgBits;
687     }
688     else fg.red = fg.green = fg.blue = 0;
689     bgBits = width * height - fgBits;
690     if (bgBits)
691     {
692         bg.red   = rbg * 257 / bgBits;
693         bg.green = gbg * 257 / bgBits;
694         bg.blue  = bbg * 257 / bgBits;
695     }
696     else bg.red = bg.green = bg.blue = 0;
697
698     info->bmiHeader.biBitCount = 1;
699     info->bmiHeader.biSizeImage = width_bytes * height;
700     SetDIBits( hdc, xor_bitmap, 0, height, xor_bits, info, DIB_RGB_COLORS );
701
702     /* generate mask from the alpha channel if we have one */
703
704     for (i = 0, ptr = color_bits; i < width * height; i++, ptr++)
705         if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
706
707     if (has_alpha)
708     {
709         memset( mask_bits, 0, width_bytes * height );
710         for (y = 0, ptr = color_bits; y < height; y++)
711             for (x = 0; x < width; x++, ptr++)
712                 if ((*ptr >> 24) > 25) /* more than 10% alpha */
713                     mask_bits[y * width_bytes + x / 8] |= 0x80 >> (x % 8);
714
715         info->bmiHeader.biBitCount = 1;
716         info->bmiHeader.biSizeImage = width_bytes * height;
717         SetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS );
718
719         wine_tsx11_lock();
720         cursor = XCreatePixmapCursor( gdi_display,
721                                       X11DRV_get_pixmap(xor_bitmap),
722                                       X11DRV_get_pixmap(icon->hbmMask),
723                                       &fg, &bg, icon->xHotspot, icon->yHotspot );
724         wine_tsx11_unlock();
725     }
726     else
727     {
728         cursor = create_cursor_from_bitmaps( xor_bitmap, icon->hbmMask, width, height, 0, 0,
729                                              &fg, &bg, icon->xHotspot, icon->yHotspot );
730     }
731
732 done:
733     DeleteObject( xor_bitmap );
734     HeapFree( GetProcessHeap(), 0, info );
735     HeapFree( GetProcessHeap(), 0, color_bits );
736     HeapFree( GetProcessHeap(), 0, xor_bits );
737     HeapFree( GetProcessHeap(), 0, mask_bits );
738     return cursor;
739 }
740
741 /***********************************************************************
742  *              create_cursor
743  *
744  * Create an X cursor from a Windows one.
745  */
746 static Cursor create_cursor( HANDLE handle )
747 {
748     Cursor cursor = 0;
749     HDC hdc;
750     ICONINFO info;
751     BITMAP bm;
752
753     if (!handle) return get_empty_cursor();
754
755     if (!(hdc = CreateCompatibleDC( 0 ))) return 0;
756     if (!GetIconInfo( handle, &info ))
757     {
758         DeleteDC( hdc );
759         return 0;
760     }
761
762     GetObjectW( info.hbmMask, sizeof(bm), &bm );
763     if (!info.hbmColor) bm.bmHeight /= 2;
764
765     /* make sure hotspot is valid */
766     if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
767     {
768         info.xHotspot = bm.bmWidth / 2;
769         info.yHotspot = bm.bmHeight / 2;
770     }
771
772     if (info.hbmColor)
773     {
774 #ifdef SONAME_LIBXCURSOR
775         if (pXcursorImageLoadCursor) cursor = create_xcursor_cursor( hdc, &info, bm.bmWidth, bm.bmHeight );
776 #endif
777         if (!cursor) cursor = create_xlib_cursor( hdc, &info, bm.bmWidth, bm.bmHeight );
778         DeleteObject( info.hbmColor );
779     }
780     else
781     {
782         XColor fg, bg;
783         fg.red = fg.green = fg.blue = 0xffff;
784         bg.red = bg.green = bg.blue = 0;
785         cursor = create_cursor_from_bitmaps( info.hbmMask, info.hbmMask, bm.bmWidth, bm.bmHeight,
786                                              bm.bmHeight, 0, &fg, &bg, info.xHotspot, info.yHotspot );
787     }
788
789     DeleteObject( info.hbmMask );
790     DeleteDC( hdc );
791     return cursor;
792 }
793
794 /***********************************************************************
795  *              DestroyCursorIcon (X11DRV.@)
796  */
797 void CDECL X11DRV_DestroyCursorIcon( HCURSOR handle )
798 {
799     Cursor cursor;
800
801     wine_tsx11_lock();
802     if (cursor_context && !XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&cursor ))
803     {
804         TRACE( "%p xid %lx\n", handle, cursor );
805         XFreeCursor( gdi_display, cursor );
806         XDeleteContext( gdi_display, (XID)handle, cursor_context );
807     }
808     wine_tsx11_unlock();
809 }
810
811 /***********************************************************************
812  *              SetCursor (X11DRV.@)
813  */
814 void CDECL X11DRV_SetCursor( HCURSOR handle )
815 {
816     if (cursor_window) SendNotifyMessageW( cursor_window, WM_X11DRV_SET_CURSOR, 0, (LPARAM)handle );
817 }
818
819 /***********************************************************************
820  *              SetCursorPos (X11DRV.@)
821  */
822 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y )
823 {
824     Display *display = thread_init_display();
825     POINT pt;
826
827     TRACE( "warping to (%d,%d)\n", x, y );
828
829     wine_tsx11_lock();
830     if (cursor_pos.x == x && cursor_pos.y == y)
831     {
832         wine_tsx11_unlock();
833         /* We still need to generate WM_MOUSEMOVE */
834         queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
835         return TRUE;
836     }
837
838     pt.x = x; pt.y = y;
839     clip_point_to_rect( &cursor_clip, &pt);
840     XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
841                   pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
842     XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
843     cursor_pos = pt;
844     wine_tsx11_unlock();
845     return TRUE;
846 }
847
848 /***********************************************************************
849  *              GetCursorPos (X11DRV.@)
850  */
851 BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos)
852 {
853     Display *display = thread_init_display();
854     Window root, child;
855     int rootX, rootY, winX, winY;
856     unsigned int xstate;
857
858     wine_tsx11_lock();
859     if ((GetTickCount() - last_time_modified > 100) &&
860         XQueryPointer( display, root_window, &root, &child,
861                        &rootX, &rootY, &winX, &winY, &xstate ))
862     {
863         update_button_state( xstate );
864         winX += virtual_screen_rect.left;
865         winY += virtual_screen_rect.top;
866         TRACE("pointer at (%d,%d)\n", winX, winY );
867         cursor_pos.x = winX;
868         cursor_pos.y = winY;
869     }
870     *pos = cursor_pos;
871     wine_tsx11_unlock();
872     return TRUE;
873 }
874
875
876 /***********************************************************************
877  *              ClipCursor (X11DRV.@)
878  *
879  * Set the cursor clipping rectangle.
880  */
881 BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
882 {
883     if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
884         cursor_clip = virtual_screen_rect;
885
886     return TRUE;
887 }
888
889 /***********************************************************************
890  *           X11DRV_ButtonPress
891  */
892 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
893 {
894     XButtonEvent *event = &xev->xbutton;
895     int buttonNum = event->button - 1;
896     WORD wData = 0;
897     POINT pt;
898
899     if (buttonNum >= NB_BUTTONS) return;
900     if (!hwnd) return;
901
902     switch (buttonNum)
903     {
904     case 3:
905         wData = WHEEL_DELTA;
906         break;
907     case 4:
908         wData = -WHEEL_DELTA;
909         break;
910     case 5:
911         wData = XBUTTON1;
912         break;
913     case 6:
914         wData = XBUTTON2;
915         break;
916     case 7:
917         wData = XBUTTON1;
918         break;
919     case 8:
920         wData = XBUTTON2;
921         break;
922     }
923
924     update_user_time( event->time );
925     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
926
927     X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
928                              pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
929 }
930
931
932 /***********************************************************************
933  *           X11DRV_ButtonRelease
934  */
935 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
936 {
937     XButtonEvent *event = &xev->xbutton;
938     int buttonNum = event->button - 1;
939     WORD wData = 0;
940     POINT pt;
941
942     if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
943     if (!hwnd) return;
944
945     switch (buttonNum)
946     {
947     case 5:
948         wData = XBUTTON1;
949         break;
950     case 6:
951         wData = XBUTTON2;
952         break;
953     case 7:
954         wData = XBUTTON1;
955         break;
956     case 8:
957         wData = XBUTTON2;
958         break;
959     }
960
961     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
962
963     X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
964                              pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
965 }
966
967
968 /***********************************************************************
969  *           X11DRV_MotionNotify
970  */
971 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
972 {
973     XMotionEvent *event = &xev->xmotion;
974     POINT pt;
975
976     TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
977
978     if (!hwnd) return;
979
980     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
981
982     X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
983                              pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
984 }
985
986
987 /***********************************************************************
988  *           X11DRV_EnterNotify
989  */
990 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
991 {
992     XCrossingEvent *event = &xev->xcrossing;
993     POINT pt;
994
995     TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
996
997     if (!hwnd) return;
998     if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
999     if (event->window == x11drv_thread_data()->grab_window) return;
1000
1001     /* simulate a mouse motion event */
1002     update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1003
1004     X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1005                              pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1006 }