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