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