user32: Make sure that SendInput always initializes the time field.
[wine/multimedia.git] / dlls / winex11.drv / mouse.c
blob922df659593b21fb99bafb0ef1a5f70b4ee2bdee
1 /*
2 * X11 mouse driver
4 * Copyright 1998 Ulrich Weigand
5 * Copyright 2007 Henri Verbeet
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <X11/Xlib.h>
26 #include <stdarg.h>
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 */
38 #define NONAMELESSUNION
39 #define NONAMELESSSTRUCT
40 #include "windef.h"
41 #include "winbase.h"
43 #include "x11drv.h"
44 #include "wine/server.h"
45 #include "wine/library.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
50 /**********************************************************************/
52 #ifndef Button6Mask
53 #define Button6Mask (1<<13)
54 #endif
55 #ifndef Button7Mask
56 #define Button7Mask (1<<14)
57 #endif
59 #define NB_BUTTONS 9 /* Windows can handle 5 buttons and the wheel too */
61 static const UINT button_down_flags[NB_BUTTONS] =
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
74 static const UINT button_up_flags[NB_BUTTONS] =
76 MOUSEEVENTF_LEFTUP,
77 MOUSEEVENTF_MIDDLEUP,
78 MOUSEEVENTF_RIGHTUP,
81 MOUSEEVENTF_XUP,
82 MOUSEEVENTF_XUP,
83 MOUSEEVENTF_XUP,
84 MOUSEEVENTF_XUP
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 );
94 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y );
97 /***********************************************************************
98 * X11DRV_Xcursor_Init
100 * Load the Xcursor library for use.
102 void X11DRV_Xcursor_Init(void)
104 #ifdef SONAME_LIBXCURSOR
105 xcursor_handle = wine_dlopen(SONAME_LIBXCURSOR, RTLD_NOW, NULL, 0);
106 if (!xcursor_handle) /* wine_dlopen failed. */
108 WARN("Xcursor failed to load. Using fallback code.\n");
109 return;
111 #define LOAD_FUNCPTR(f) \
112 p##f = wine_dlsym(xcursor_handle, #f, NULL, 0)
114 LOAD_FUNCPTR(XcursorImageCreate);
115 LOAD_FUNCPTR(XcursorImageDestroy);
116 LOAD_FUNCPTR(XcursorImageLoadCursor);
117 #undef LOAD_FUNCPTR
118 #endif /* SONAME_LIBXCURSOR */
122 /***********************************************************************
123 * get_coords
125 * get the coordinates of a mouse event
127 static inline void get_coords( HWND hwnd, Window window, int x, int y, POINT *pt )
129 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
131 if (!data) return;
133 if (window == data->client_window)
135 pt->x = x + data->client_rect.left;
136 pt->y = y + data->client_rect.top;
138 else
140 pt->x = x + data->whole_rect.left;
141 pt->y = y + data->whole_rect.top;
145 /***********************************************************************
146 * clip_point_to_rect
148 * Clip point to the provided rectangle
150 static inline void clip_point_to_rect( LPCRECT rect, LPPOINT pt )
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;
158 /***********************************************************************
159 * update_button_state
161 * Update the button state with what X provides us
163 static inline void update_button_state( unsigned int state )
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 */
171 /***********************************************************************
172 * get_empty_cursor
174 static Cursor get_empty_cursor(void)
176 static Cursor cursor;
177 static const char data[] = { 0 };
179 wine_tsx11_lock();
180 if (!cursor)
182 XColor bg;
183 Pixmap pixmap;
185 bg.red = bg.green = bg.blue = 0x0000;
186 pixmap = XCreateBitmapFromData( gdi_display, root_window, data, 1, 1 );
187 if (pixmap)
189 cursor = XCreatePixmapCursor( gdi_display, pixmap, pixmap, &bg, &bg, 0, 0 );
190 XFreePixmap( gdi_display, pixmap );
193 wine_tsx11_unlock();
194 return cursor;
197 /***********************************************************************
198 * set_window_cursor
200 void set_window_cursor( HWND hwnd, HCURSOR handle )
202 struct x11drv_win_data *data;
203 Cursor cursor, prev;
205 if (!(data = X11DRV_get_win_data( hwnd ))) return;
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 ))
211 /* try to create it */
212 wine_tsx11_unlock();
213 if (!(cursor = create_cursor( handle ))) return;
215 wine_tsx11_lock();
216 if (!cursor_context) cursor_context = XUniqueContext();
217 if (!XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&prev ))
219 /* someone else was here first */
220 XFreeCursor( gdi_display, cursor );
221 cursor = prev;
223 else
225 XSaveContext( gdi_display, (XID)handle, cursor_context, (char *)cursor );
226 TRACE( "cursor %p created %lx\n", handle, cursor );
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();
237 /***********************************************************************
238 * update_mouse_state
240 * Update the various window states on a mouse event.
242 static void update_mouse_state( HWND hwnd, Window window, int x, int y, unsigned int state, POINT *pt )
244 struct x11drv_thread_data *data = x11drv_thread_data();
246 get_coords( hwnd, window, x, y, pt );
248 cursor_window = hwnd;
250 /* update the wine server Z-order */
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)))
256 SERVER_START_REQ( update_window_zorder )
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 );
265 SERVER_END_REQ;
270 /***********************************************************************
271 * get_key_state
273 static WORD get_key_state(void)
275 WORD ret = 0;
277 if (GetSystemMetrics( SM_SWAPBUTTON ))
279 if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_LBUTTON;
280 if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_RBUTTON;
282 else
284 if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_LBUTTON;
285 if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_RBUTTON;
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;
296 /***********************************************************************
297 * queue_raw_mouse_message
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 )
302 MSLLHOOKSTRUCT hook;
303 HCURSOR cursor;
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;
312 last_time_modified = GetTickCount();
313 if (HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE ))
314 message = 0; /* ignore it */
316 SERVER_START_REQ( send_hardware_message )
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;
330 SERVER_END_REQ;
332 if (hwnd)
334 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
335 if (data && cursor != data->cursor) set_window_cursor( hwnd, cursor );
340 /***********************************************************************
341 * X11DRV_send_mouse_input
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 )
346 POINT pt;
348 if (!time) time = GetTickCount();
350 if (flags & MOUSEEVENTF_MOVE && flags & MOUSEEVENTF_ABSOLUTE)
352 if (injected_flags & LLMHF_INJECTED)
354 pt.x = (x * screen_width) >> 16;
355 pt.y = (y * screen_height) >> 16;
357 else
359 pt.x = x;
360 pt.y = y;
361 wine_tsx11_lock();
362 if (cursor_pos.x == x && cursor_pos.y == y &&
363 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
364 flags &= ~MOUSEEVENTF_MOVE;
365 wine_tsx11_unlock();
368 else if (flags & MOUSEEVENTF_MOVE)
370 int accel[3], xMult = 1, yMult = 1;
372 /* dx and dy can be negative numbers for relative movements */
373 SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
375 if (abs(x) > accel[0] && accel[2] != 0)
377 xMult = 2;
378 if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
380 if (abs(y) > accel[0] && accel[2] != 0)
382 yMult = 2;
383 if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
386 wine_tsx11_lock();
387 pt.x = cursor_pos.x + (long)x * xMult;
388 pt.y = cursor_pos.y + (long)y * yMult;
389 wine_tsx11_unlock();
391 else
393 wine_tsx11_lock();
394 pt = cursor_pos;
395 wine_tsx11_unlock();
398 if (flags & MOUSEEVENTF_MOVE)
400 queue_raw_mouse_message( WM_MOUSEMOVE, hwnd, pt.x, pt.y, data, time,
401 extra_info, injected_flags );
402 if ((injected_flags & LLMHF_INJECTED) &&
403 ((flags & MOUSEEVENTF_ABSOLUTE) || x || y)) /* we have to actually move the cursor */
405 X11DRV_SetCursorPos( pt.x, pt.y );
407 else
409 wine_tsx11_lock();
410 clip_point_to_rect( &cursor_clip, &pt);
411 cursor_pos = pt;
412 wine_tsx11_unlock();
415 if (flags & MOUSEEVENTF_LEFTDOWN)
417 key_state_table[VK_LBUTTON] |= 0xc0;
418 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
419 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
421 if (flags & MOUSEEVENTF_LEFTUP)
423 key_state_table[VK_LBUTTON] &= ~0x80;
424 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
425 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
427 if (flags & MOUSEEVENTF_RIGHTDOWN)
429 key_state_table[VK_RBUTTON] |= 0xc0;
430 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
431 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
433 if (flags & MOUSEEVENTF_RIGHTUP)
435 key_state_table[VK_RBUTTON] &= ~0x80;
436 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
437 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
439 if (flags & MOUSEEVENTF_MIDDLEDOWN)
441 key_state_table[VK_MBUTTON] |= 0xc0;
442 queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
443 extra_info, injected_flags );
445 if (flags & MOUSEEVENTF_MIDDLEUP)
447 key_state_table[VK_MBUTTON] &= ~0x80;
448 queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
449 extra_info, injected_flags );
451 if (flags & MOUSEEVENTF_WHEEL)
453 queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
454 extra_info, injected_flags );
456 if (flags & MOUSEEVENTF_XDOWN)
458 key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
459 queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
460 extra_info, injected_flags );
462 if (flags & MOUSEEVENTF_XUP)
464 key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
465 queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
466 extra_info, injected_flags );
470 #ifdef SONAME_LIBXCURSOR
472 /***********************************************************************
473 * create_xcursor_cursor
475 * Use Xcursor to create an X cursor from a Windows one.
477 static Cursor create_xcursor_cursor( HDC hdc, ICONINFO *icon, int width, int height )
479 int x, y, i, has_alpha;
480 BITMAPINFO *info;
481 Cursor cursor;
482 XcursorImage *image;
483 XcursorPixel *ptr;
485 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return 0;
487 wine_tsx11_lock();
488 image = pXcursorImageCreate( width, height );
489 wine_tsx11_unlock();
490 if (!image)
492 HeapFree( GetProcessHeap(), 0, info );
493 return 0;
496 image->xhot = icon->xHotspot;
497 image->yhot = icon->yHotspot;
498 image->delay = 0;
500 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
501 info->bmiHeader.biWidth = width;
502 info->bmiHeader.biHeight = -height;
503 info->bmiHeader.biPlanes = 1;
504 info->bmiHeader.biBitCount = 32;
505 info->bmiHeader.biCompression = BI_RGB;
506 info->bmiHeader.biSizeImage = width * height * 4;
507 info->bmiHeader.biXPelsPerMeter = 0;
508 info->bmiHeader.biYPelsPerMeter = 0;
509 info->bmiHeader.biClrUsed = 0;
510 info->bmiHeader.biClrImportant = 0;
511 GetDIBits( hdc, icon->hbmColor, 0, height, image->pixels, info, DIB_RGB_COLORS );
513 for (i = 0, ptr = image->pixels; i < width * height; i++, ptr++)
514 if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
516 if (!has_alpha)
518 unsigned char *mask_bits;
519 unsigned int width_bytes = (width + 31) / 32 * 4;
521 /* generate alpha channel from the mask */
522 info->bmiHeader.biBitCount = 1;
523 info->bmiHeader.biSizeImage = width_bytes * height;
524 if ((mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage )))
526 GetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS );
527 for (y = 0, ptr = image->pixels; y < height; y++)
528 for (x = 0; x < width; x++, ptr++)
529 if (!((mask_bits[y * width_bytes + x / 8] << (x % 8)) & 0x80))
530 *ptr |= 0xff000000;
531 HeapFree( GetProcessHeap(), 0, mask_bits );
534 HeapFree( GetProcessHeap(), 0, info );
536 wine_tsx11_lock();
537 cursor = pXcursorImageLoadCursor( gdi_display, image );
538 pXcursorImageDestroy( image );
539 wine_tsx11_unlock();
540 return cursor;
543 #endif /* SONAME_LIBXCURSOR */
546 /***********************************************************************
547 * create_cursor_from_bitmaps
549 * Create an X11 cursor from source bitmaps.
551 static Cursor create_cursor_from_bitmaps( HBITMAP src_xor, HBITMAP src_and, int width, int height,
552 int xor_y, int and_y, XColor *fg, XColor *bg,
553 int hotspot_x, int hotspot_y )
555 HDC src = 0, dst = 0;
556 HBITMAP bits = 0, mask = 0, mask_inv = 0;
557 Cursor cursor = 0;
559 if (!(src = CreateCompatibleDC( 0 ))) goto done;
560 if (!(dst = CreateCompatibleDC( 0 ))) goto done;
562 if (!(bits = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
563 if (!(mask = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
564 if (!(mask_inv = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
566 /* We have to do some magic here, as cursors are not fully
567 * compatible between Windows and X11. Under X11, there are
568 * only 3 possible color cursor: black, white and masked. So
569 * we map the 4th Windows color (invert the bits on the screen)
570 * to black and an additional white bit on an other place
571 * (+1,+1). This require some boolean arithmetic:
573 * Windows | X11
574 * And Xor Result | Bits Mask Result
575 * 0 0 black | 0 1 background
576 * 0 1 white | 1 1 foreground
577 * 1 0 no change | X 0 no change
578 * 1 1 inverted | 0 1 background
580 * which gives:
581 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
582 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
584 SelectObject( src, src_and );
585 SelectObject( dst, bits );
586 BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
587 SelectObject( dst, mask );
588 BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
589 SelectObject( dst, mask_inv );
590 BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
591 SelectObject( src, src_xor );
592 BitBlt( dst, 0, 0, width, height, src, 0, xor_y, SRCAND /* src & dst */ );
593 SelectObject( dst, bits );
594 BitBlt( dst, 0, 0, width, height, src, 0, xor_y, SRCERASE /* src & ~dst */ );
595 SelectObject( dst, mask );
596 BitBlt( dst, 0, 0, width, height, src, 0, xor_y, 0xdd0228 /* src | ~dst */ );
597 /* additional white */
598 SelectObject( src, mask_inv );
599 BitBlt( dst, 1, 1, width, height, src, 0, 0, SRCPAINT /* src | dst */);
600 SelectObject( dst, bits );
601 BitBlt( dst, 1, 1, width, height, src, 0, 0, SRCPAINT /* src | dst */ );
603 wine_tsx11_lock();
604 cursor = XCreatePixmapCursor( gdi_display, X11DRV_get_pixmap(bits), X11DRV_get_pixmap(mask),
605 fg, bg, hotspot_x, hotspot_y );
606 wine_tsx11_unlock();
608 done:
609 DeleteDC( src );
610 DeleteDC( dst );
611 DeleteObject( bits );
612 DeleteObject( mask );
613 DeleteObject( mask_inv );
614 return cursor;
617 /***********************************************************************
618 * create_xlib_cursor
620 * Create an X cursor from a Windows one.
622 static Cursor create_xlib_cursor( HDC hdc, ICONINFO *icon, int width, int height )
624 XColor fg, bg;
625 Cursor cursor = None;
626 HBITMAP xor_bitmap = 0;
627 BITMAPINFO *info;
628 unsigned int *color_bits = NULL, *ptr;
629 unsigned char *mask_bits = NULL, *xor_bits = NULL;
630 int i, x, y, has_alpha = 0;
631 int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
632 unsigned int width_bytes = (width + 31) / 32 * 4;
634 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] ))))
635 return FALSE;
636 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
637 info->bmiHeader.biWidth = width;
638 info->bmiHeader.biHeight = -height;
639 info->bmiHeader.biPlanes = 1;
640 info->bmiHeader.biBitCount = 1;
641 info->bmiHeader.biCompression = BI_RGB;
642 info->bmiHeader.biSizeImage = width_bytes * height;
643 info->bmiHeader.biXPelsPerMeter = 0;
644 info->bmiHeader.biYPelsPerMeter = 0;
645 info->bmiHeader.biClrUsed = 0;
646 info->bmiHeader.biClrImportant = 0;
648 if (!(mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done;
649 if (!GetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS )) goto done;
651 info->bmiHeader.biBitCount = 32;
652 info->bmiHeader.biSizeImage = width * height * 4;
653 if (!(color_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done;
654 if (!(xor_bits = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, width_bytes * height ))) goto done;
655 GetDIBits( hdc, icon->hbmColor, 0, height, color_bits, info, DIB_RGB_COLORS );
657 /* compute fg/bg color and xor bitmap based on average of the color values */
659 if (!(xor_bitmap = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
660 rfg = gfg = bfg = rbg = gbg = bbg = fgBits = 0;
661 for (y = 0, ptr = color_bits; y < height; y++)
663 for (x = 0; x < width; x++, ptr++)
665 int red = (*ptr >> 16) & 0xff;
666 int green = (*ptr >> 8) & 0xff;
667 int blue = (*ptr >> 0) & 0xff;
668 if (red + green + blue > 0x40)
670 rfg += red;
671 gfg += green;
672 bfg += blue;
673 fgBits++;
674 xor_bits[y * width_bytes + x / 8] |= 0x80 >> (x % 8);
676 else
678 rbg += red;
679 gbg += green;
680 bbg += blue;
684 if (fgBits)
686 fg.red = rfg * 257 / fgBits;
687 fg.green = gfg * 257 / fgBits;
688 fg.blue = bfg * 257 / fgBits;
690 else fg.red = fg.green = fg.blue = 0;
691 bgBits = width * height - fgBits;
692 if (bgBits)
694 bg.red = rbg * 257 / bgBits;
695 bg.green = gbg * 257 / bgBits;
696 bg.blue = bbg * 257 / bgBits;
698 else bg.red = bg.green = bg.blue = 0;
700 info->bmiHeader.biBitCount = 1;
701 info->bmiHeader.biSizeImage = width_bytes * height;
702 SetDIBits( hdc, xor_bitmap, 0, height, xor_bits, info, DIB_RGB_COLORS );
704 /* generate mask from the alpha channel if we have one */
706 for (i = 0, ptr = color_bits; i < width * height; i++, ptr++)
707 if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
709 if (has_alpha)
711 memset( mask_bits, 0, width_bytes * height );
712 for (y = 0, ptr = color_bits; y < height; y++)
713 for (x = 0; x < width; x++, ptr++)
714 if ((*ptr >> 24) > 25) /* more than 10% alpha */
715 mask_bits[y * width_bytes + x / 8] |= 0x80 >> (x % 8);
717 info->bmiHeader.biBitCount = 1;
718 info->bmiHeader.biSizeImage = width_bytes * height;
719 SetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS );
721 wine_tsx11_lock();
722 cursor = XCreatePixmapCursor( gdi_display,
723 X11DRV_get_pixmap(xor_bitmap),
724 X11DRV_get_pixmap(icon->hbmMask),
725 &fg, &bg, icon->xHotspot, icon->yHotspot );
726 wine_tsx11_unlock();
728 else
730 cursor = create_cursor_from_bitmaps( xor_bitmap, icon->hbmMask, width, height, 0, 0,
731 &fg, &bg, icon->xHotspot, icon->yHotspot );
734 done:
735 DeleteObject( xor_bitmap );
736 HeapFree( GetProcessHeap(), 0, info );
737 HeapFree( GetProcessHeap(), 0, color_bits );
738 HeapFree( GetProcessHeap(), 0, xor_bits );
739 HeapFree( GetProcessHeap(), 0, mask_bits );
740 return cursor;
743 /***********************************************************************
744 * create_cursor
746 * Create an X cursor from a Windows one.
748 static Cursor create_cursor( HANDLE handle )
750 Cursor cursor = 0;
751 HDC hdc;
752 ICONINFO info;
753 BITMAP bm;
755 if (!handle) return get_empty_cursor();
757 if (!(hdc = CreateCompatibleDC( 0 ))) return 0;
758 if (!GetIconInfo( handle, &info ))
760 DeleteDC( hdc );
761 return 0;
764 GetObjectW( info.hbmMask, sizeof(bm), &bm );
765 if (!info.hbmColor) bm.bmHeight /= 2;
767 /* make sure hotspot is valid */
768 if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
770 info.xHotspot = bm.bmWidth / 2;
771 info.yHotspot = bm.bmHeight / 2;
774 if (info.hbmColor)
776 #ifdef SONAME_LIBXCURSOR
777 if (pXcursorImageLoadCursor) cursor = create_xcursor_cursor( hdc, &info, bm.bmWidth, bm.bmHeight );
778 #endif
779 if (!cursor) cursor = create_xlib_cursor( hdc, &info, bm.bmWidth, bm.bmHeight );
780 DeleteObject( info.hbmColor );
782 else
784 XColor fg, bg;
785 fg.red = fg.green = fg.blue = 0xffff;
786 bg.red = bg.green = bg.blue = 0;
787 cursor = create_cursor_from_bitmaps( info.hbmMask, info.hbmMask, bm.bmWidth, bm.bmHeight,
788 bm.bmHeight, 0, &fg, &bg, info.xHotspot, info.yHotspot );
791 DeleteObject( info.hbmMask );
792 DeleteDC( hdc );
793 return cursor;
796 /***********************************************************************
797 * DestroyCursorIcon (X11DRV.@)
799 void CDECL X11DRV_DestroyCursorIcon( HCURSOR handle )
801 Cursor cursor;
803 wine_tsx11_lock();
804 if (cursor_context && !XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&cursor ))
806 TRACE( "%p xid %lx\n", handle, cursor );
807 XFreeCursor( gdi_display, cursor );
808 XDeleteContext( gdi_display, (XID)handle, cursor_context );
810 wine_tsx11_unlock();
813 /***********************************************************************
814 * SetCursor (X11DRV.@)
816 void CDECL X11DRV_SetCursor( HCURSOR handle )
818 if (cursor_window) SendNotifyMessageW( cursor_window, WM_X11DRV_SET_CURSOR, 0, (LPARAM)handle );
821 /***********************************************************************
822 * SetCursorPos (X11DRV.@)
824 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y )
826 Display *display = thread_init_display();
827 POINT pt;
829 TRACE( "warping to (%d,%d)\n", x, y );
831 wine_tsx11_lock();
832 if (cursor_pos.x == x && cursor_pos.y == y)
834 wine_tsx11_unlock();
835 /* We still need to generate WM_MOUSEMOVE */
836 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
837 return TRUE;
840 pt.x = x; pt.y = y;
841 clip_point_to_rect( &cursor_clip, &pt);
842 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
843 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
844 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
845 cursor_pos = pt;
846 wine_tsx11_unlock();
847 return TRUE;
850 /***********************************************************************
851 * GetCursorPos (X11DRV.@)
853 BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos)
855 Display *display = thread_init_display();
856 Window root, child;
857 int rootX, rootY, winX, winY;
858 unsigned int xstate;
860 wine_tsx11_lock();
861 if ((GetTickCount() - last_time_modified > 100) &&
862 XQueryPointer( display, root_window, &root, &child,
863 &rootX, &rootY, &winX, &winY, &xstate ))
865 update_button_state( xstate );
866 winX += virtual_screen_rect.left;
867 winY += virtual_screen_rect.top;
868 TRACE("pointer at (%d,%d)\n", winX, winY );
869 cursor_pos.x = winX;
870 cursor_pos.y = winY;
872 *pos = cursor_pos;
873 wine_tsx11_unlock();
874 return TRUE;
878 /***********************************************************************
879 * ClipCursor (X11DRV.@)
881 * Set the cursor clipping rectangle.
883 BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
885 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
886 cursor_clip = virtual_screen_rect;
888 return TRUE;
891 /***********************************************************************
892 * X11DRV_ButtonPress
894 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
896 XButtonEvent *event = &xev->xbutton;
897 int buttonNum = event->button - 1;
898 WORD wData = 0;
899 POINT pt;
901 if (buttonNum >= NB_BUTTONS) return;
902 if (!hwnd) return;
904 switch (buttonNum)
906 case 3:
907 wData = WHEEL_DELTA;
908 break;
909 case 4:
910 wData = -WHEEL_DELTA;
911 break;
912 case 5:
913 wData = XBUTTON1;
914 break;
915 case 6:
916 wData = XBUTTON2;
917 break;
918 case 7:
919 wData = XBUTTON1;
920 break;
921 case 8:
922 wData = XBUTTON2;
923 break;
926 update_user_time( event->time );
927 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
929 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
930 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
934 /***********************************************************************
935 * X11DRV_ButtonRelease
937 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
939 XButtonEvent *event = &xev->xbutton;
940 int buttonNum = event->button - 1;
941 WORD wData = 0;
942 POINT pt;
944 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
945 if (!hwnd) return;
947 switch (buttonNum)
949 case 5:
950 wData = XBUTTON1;
951 break;
952 case 6:
953 wData = XBUTTON2;
954 break;
955 case 7:
956 wData = XBUTTON1;
957 break;
958 case 8:
959 wData = XBUTTON2;
960 break;
963 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
965 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
966 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
970 /***********************************************************************
971 * X11DRV_MotionNotify
973 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
975 XMotionEvent *event = &xev->xmotion;
976 POINT pt;
978 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
980 if (!hwnd) return;
982 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
984 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
985 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
989 /***********************************************************************
990 * X11DRV_EnterNotify
992 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
994 XCrossingEvent *event = &xev->xcrossing;
995 POINT pt;
997 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
999 if (!hwnd) return;
1000 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
1001 if (event->window == x11drv_thread_data()->grab_window) return;
1003 /* simulate a mouse motion event */
1004 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1006 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1007 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );