winex11: Set the user time property on top-level windows.
[wine/multimedia.git] / dlls / winex11.drv / mouse.c
blobcf988c9c7005b837e322fc2f1d8d31ca6b87edd8
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 (flags & MOUSEEVENTF_MOVE && flags & MOUSEEVENTF_ABSOLUTE)
350 if (injected_flags & LLMHF_INJECTED)
352 pt.x = (x * screen_width) >> 16;
353 pt.y = (y * screen_height) >> 16;
355 else
357 pt.x = x;
358 pt.y = y;
359 wine_tsx11_lock();
360 if (cursor_pos.x == x && cursor_pos.y == y &&
361 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
362 flags &= ~MOUSEEVENTF_MOVE;
363 wine_tsx11_unlock();
366 else if (flags & MOUSEEVENTF_MOVE)
368 int accel[3], xMult = 1, yMult = 1;
370 /* dx and dy can be negative numbers for relative movements */
371 SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
373 if (abs(x) > accel[0] && accel[2] != 0)
375 xMult = 2;
376 if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
378 if (abs(y) > accel[0] && accel[2] != 0)
380 yMult = 2;
381 if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
384 wine_tsx11_lock();
385 pt.x = cursor_pos.x + (long)x * xMult;
386 pt.y = cursor_pos.y + (long)y * yMult;
387 wine_tsx11_unlock();
389 else
391 wine_tsx11_lock();
392 pt = cursor_pos;
393 wine_tsx11_unlock();
396 if (flags & MOUSEEVENTF_MOVE)
398 queue_raw_mouse_message( WM_MOUSEMOVE, hwnd, pt.x, pt.y, data, time,
399 extra_info, injected_flags );
400 if ((injected_flags & LLMHF_INJECTED) &&
401 ((flags & MOUSEEVENTF_ABSOLUTE) || x || y)) /* we have to actually move the cursor */
403 X11DRV_SetCursorPos( pt.x, pt.y );
405 else
407 wine_tsx11_lock();
408 clip_point_to_rect( &cursor_clip, &pt);
409 cursor_pos = pt;
410 wine_tsx11_unlock();
413 if (flags & MOUSEEVENTF_LEFTDOWN)
415 key_state_table[VK_LBUTTON] |= 0xc0;
416 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
417 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
419 if (flags & MOUSEEVENTF_LEFTUP)
421 key_state_table[VK_LBUTTON] &= ~0x80;
422 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
423 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
425 if (flags & MOUSEEVENTF_RIGHTDOWN)
427 key_state_table[VK_RBUTTON] |= 0xc0;
428 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
429 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
431 if (flags & MOUSEEVENTF_RIGHTUP)
433 key_state_table[VK_RBUTTON] &= ~0x80;
434 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
435 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
437 if (flags & MOUSEEVENTF_MIDDLEDOWN)
439 key_state_table[VK_MBUTTON] |= 0xc0;
440 queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
441 extra_info, injected_flags );
443 if (flags & MOUSEEVENTF_MIDDLEUP)
445 key_state_table[VK_MBUTTON] &= ~0x80;
446 queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
447 extra_info, injected_flags );
449 if (flags & MOUSEEVENTF_WHEEL)
451 queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
452 extra_info, injected_flags );
454 if (flags & MOUSEEVENTF_XDOWN)
456 key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
457 queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
458 extra_info, injected_flags );
460 if (flags & MOUSEEVENTF_XUP)
462 key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
463 queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
464 extra_info, injected_flags );
468 #ifdef SONAME_LIBXCURSOR
470 /***********************************************************************
471 * create_xcursor_cursor
473 * Use Xcursor to create an X cursor from a Windows one.
475 static Cursor create_xcursor_cursor( HDC hdc, ICONINFO *icon, int width, int height )
477 int x, y, i, has_alpha;
478 BITMAPINFO *info;
479 Cursor cursor;
480 XcursorImage *image;
481 XcursorPixel *ptr;
483 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return 0;
485 wine_tsx11_lock();
486 image = pXcursorImageCreate( width, height );
487 wine_tsx11_unlock();
488 if (!image)
490 HeapFree( GetProcessHeap(), 0, info );
491 return 0;
494 image->xhot = icon->xHotspot;
495 image->yhot = icon->yHotspot;
496 image->delay = 0;
498 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
499 info->bmiHeader.biWidth = width;
500 info->bmiHeader.biHeight = -height;
501 info->bmiHeader.biPlanes = 1;
502 info->bmiHeader.biBitCount = 32;
503 info->bmiHeader.biCompression = BI_RGB;
504 info->bmiHeader.biSizeImage = width * height * 4;
505 info->bmiHeader.biXPelsPerMeter = 0;
506 info->bmiHeader.biYPelsPerMeter = 0;
507 info->bmiHeader.biClrUsed = 0;
508 info->bmiHeader.biClrImportant = 0;
509 GetDIBits( hdc, icon->hbmColor, 0, height, image->pixels, info, DIB_RGB_COLORS );
511 for (i = 0, ptr = image->pixels; i < width * height; i++, ptr++)
512 if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
514 if (!has_alpha)
516 unsigned char *mask_bits;
517 unsigned int width_bytes = (width + 31) / 32 * 4;
519 /* generate alpha channel from the mask */
520 info->bmiHeader.biBitCount = 1;
521 info->bmiHeader.biSizeImage = width_bytes * height;
522 if ((mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage )))
524 GetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS );
525 for (y = 0, ptr = image->pixels; y < height; y++)
526 for (x = 0; x < width; x++, ptr++)
527 if (!((mask_bits[y * width_bytes + x / 8] << (x % 8)) & 0x80))
528 *ptr |= 0xff000000;
529 HeapFree( GetProcessHeap(), 0, mask_bits );
532 HeapFree( GetProcessHeap(), 0, info );
534 wine_tsx11_lock();
535 cursor = pXcursorImageLoadCursor( gdi_display, image );
536 pXcursorImageDestroy( image );
537 wine_tsx11_unlock();
538 return cursor;
541 #endif /* SONAME_LIBXCURSOR */
544 /***********************************************************************
545 * create_cursor_from_bitmaps
547 * Create an X11 cursor from source bitmaps.
549 static Cursor create_cursor_from_bitmaps( HBITMAP src_xor, HBITMAP src_and, int width, int height,
550 int xor_y, int and_y, XColor *fg, XColor *bg,
551 int hotspot_x, int hotspot_y )
553 HDC src = 0, dst = 0;
554 HBITMAP bits = 0, mask = 0, mask_inv = 0;
555 Cursor cursor = 0;
557 if (!(src = CreateCompatibleDC( 0 ))) goto done;
558 if (!(dst = CreateCompatibleDC( 0 ))) goto done;
560 if (!(bits = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
561 if (!(mask = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
562 if (!(mask_inv = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
564 /* We have to do some magic here, as cursors are not fully
565 * compatible between Windows and X11. Under X11, there are
566 * only 3 possible color cursor: black, white and masked. So
567 * we map the 4th Windows color (invert the bits on the screen)
568 * to black and an additional white bit on an other place
569 * (+1,+1). This require some boolean arithmetic:
571 * Windows | X11
572 * And Xor Result | Bits Mask Result
573 * 0 0 black | 0 1 background
574 * 0 1 white | 1 1 foreground
575 * 1 0 no change | X 0 no change
576 * 1 1 inverted | 0 1 background
578 * which gives:
579 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
580 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
582 SelectObject( src, src_and );
583 SelectObject( dst, bits );
584 BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
585 SelectObject( dst, mask );
586 BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
587 SelectObject( dst, mask_inv );
588 BitBlt( dst, 0, 0, width, height, src, 0, and_y, SRCCOPY );
589 SelectObject( src, src_xor );
590 BitBlt( dst, 0, 0, width, height, src, 0, xor_y, SRCAND /* src & dst */ );
591 SelectObject( dst, bits );
592 BitBlt( dst, 0, 0, width, height, src, 0, xor_y, SRCERASE /* src & ~dst */ );
593 SelectObject( dst, mask );
594 BitBlt( dst, 0, 0, width, height, src, 0, xor_y, 0xdd0228 /* src | ~dst */ );
595 /* additional white */
596 SelectObject( src, mask_inv );
597 BitBlt( dst, 1, 1, width, height, src, 0, 0, SRCPAINT /* src | dst */);
598 SelectObject( dst, bits );
599 BitBlt( dst, 1, 1, width, height, src, 0, 0, SRCPAINT /* src | dst */ );
601 wine_tsx11_lock();
602 cursor = XCreatePixmapCursor( gdi_display, X11DRV_get_pixmap(bits), X11DRV_get_pixmap(mask),
603 fg, bg, hotspot_x, hotspot_y );
604 wine_tsx11_unlock();
606 done:
607 DeleteDC( src );
608 DeleteDC( dst );
609 DeleteObject( bits );
610 DeleteObject( mask );
611 DeleteObject( mask_inv );
612 return cursor;
615 /***********************************************************************
616 * create_xlib_cursor
618 * Create an X cursor from a Windows one.
620 static Cursor create_xlib_cursor( HDC hdc, ICONINFO *icon, int width, int height )
622 XColor fg, bg;
623 Cursor cursor = None;
624 HBITMAP xor_bitmap = 0;
625 BITMAPINFO *info;
626 unsigned int *color_bits = NULL, *ptr;
627 unsigned char *mask_bits = NULL, *xor_bits = NULL;
628 int i, x, y, has_alpha = 0;
629 int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
630 unsigned int width_bytes = (width + 31) / 32 * 4;
632 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] ))))
633 return FALSE;
634 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
635 info->bmiHeader.biWidth = width;
636 info->bmiHeader.biHeight = -height;
637 info->bmiHeader.biPlanes = 1;
638 info->bmiHeader.biBitCount = 1;
639 info->bmiHeader.biCompression = BI_RGB;
640 info->bmiHeader.biSizeImage = width_bytes * height;
641 info->bmiHeader.biXPelsPerMeter = 0;
642 info->bmiHeader.biYPelsPerMeter = 0;
643 info->bmiHeader.biClrUsed = 0;
644 info->bmiHeader.biClrImportant = 0;
646 if (!(mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done;
647 if (!GetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS )) goto done;
649 info->bmiHeader.biBitCount = 32;
650 info->bmiHeader.biSizeImage = width * height * 4;
651 if (!(color_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done;
652 if (!(xor_bits = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, width_bytes * height ))) goto done;
653 GetDIBits( hdc, icon->hbmColor, 0, height, color_bits, info, DIB_RGB_COLORS );
655 /* compute fg/bg color and xor bitmap based on average of the color values */
657 if (!(xor_bitmap = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
658 rfg = gfg = bfg = rbg = gbg = bbg = fgBits = 0;
659 for (y = 0, ptr = color_bits; y < height; y++)
661 for (x = 0; x < width; x++, ptr++)
663 int red = (*ptr >> 16) & 0xff;
664 int green = (*ptr >> 8) & 0xff;
665 int blue = (*ptr >> 0) & 0xff;
666 if (red + green + blue > 0x40)
668 rfg += red;
669 gfg += green;
670 bfg += blue;
671 fgBits++;
672 xor_bits[y * width_bytes + x / 8] |= 0x80 >> (x % 8);
674 else
676 rbg += red;
677 gbg += green;
678 bbg += blue;
682 if (fgBits)
684 fg.red = rfg * 257 / fgBits;
685 fg.green = gfg * 257 / fgBits;
686 fg.blue = bfg * 257 / fgBits;
688 else fg.red = fg.green = fg.blue = 0;
689 bgBits = width * height - fgBits;
690 if (bgBits)
692 bg.red = rbg * 257 / bgBits;
693 bg.green = gbg * 257 / bgBits;
694 bg.blue = bbg * 257 / bgBits;
696 else bg.red = bg.green = bg.blue = 0;
698 info->bmiHeader.biBitCount = 1;
699 info->bmiHeader.biSizeImage = width_bytes * height;
700 SetDIBits( hdc, xor_bitmap, 0, height, xor_bits, info, DIB_RGB_COLORS );
702 /* generate mask from the alpha channel if we have one */
704 for (i = 0, ptr = color_bits; i < width * height; i++, ptr++)
705 if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
707 if (has_alpha)
709 memset( mask_bits, 0, width_bytes * height );
710 for (y = 0, ptr = color_bits; y < height; y++)
711 for (x = 0; x < width; x++, ptr++)
712 if ((*ptr >> 24) > 25) /* more than 10% alpha */
713 mask_bits[y * width_bytes + x / 8] |= 0x80 >> (x % 8);
715 info->bmiHeader.biBitCount = 1;
716 info->bmiHeader.biSizeImage = width_bytes * height;
717 SetDIBits( hdc, icon->hbmMask, 0, height, mask_bits, info, DIB_RGB_COLORS );
719 wine_tsx11_lock();
720 cursor = XCreatePixmapCursor( gdi_display,
721 X11DRV_get_pixmap(xor_bitmap),
722 X11DRV_get_pixmap(icon->hbmMask),
723 &fg, &bg, icon->xHotspot, icon->yHotspot );
724 wine_tsx11_unlock();
726 else
728 cursor = create_cursor_from_bitmaps( xor_bitmap, icon->hbmMask, width, height, 0, 0,
729 &fg, &bg, icon->xHotspot, icon->yHotspot );
732 done:
733 DeleteObject( xor_bitmap );
734 HeapFree( GetProcessHeap(), 0, info );
735 HeapFree( GetProcessHeap(), 0, color_bits );
736 HeapFree( GetProcessHeap(), 0, xor_bits );
737 HeapFree( GetProcessHeap(), 0, mask_bits );
738 return cursor;
741 /***********************************************************************
742 * create_cursor
744 * Create an X cursor from a Windows one.
746 static Cursor create_cursor( HANDLE handle )
748 Cursor cursor = 0;
749 HDC hdc;
750 ICONINFO info;
751 BITMAP bm;
753 if (!handle) return get_empty_cursor();
755 if (!(hdc = CreateCompatibleDC( 0 ))) return 0;
756 if (!GetIconInfo( handle, &info ))
758 DeleteDC( hdc );
759 return 0;
762 GetObjectW( info.hbmMask, sizeof(bm), &bm );
763 if (!info.hbmColor) bm.bmHeight /= 2;
765 /* make sure hotspot is valid */
766 if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
768 info.xHotspot = bm.bmWidth / 2;
769 info.yHotspot = bm.bmHeight / 2;
772 if (info.hbmColor)
774 #ifdef SONAME_LIBXCURSOR
775 if (pXcursorImageLoadCursor) cursor = create_xcursor_cursor( hdc, &info, bm.bmWidth, bm.bmHeight );
776 #endif
777 if (!cursor) cursor = create_xlib_cursor( hdc, &info, bm.bmWidth, bm.bmHeight );
778 DeleteObject( info.hbmColor );
780 else
782 XColor fg, bg;
783 fg.red = fg.green = fg.blue = 0xffff;
784 bg.red = bg.green = bg.blue = 0;
785 cursor = create_cursor_from_bitmaps( info.hbmMask, info.hbmMask, bm.bmWidth, bm.bmHeight,
786 bm.bmHeight, 0, &fg, &bg, info.xHotspot, info.yHotspot );
789 DeleteObject( info.hbmMask );
790 DeleteDC( hdc );
791 return cursor;
794 /***********************************************************************
795 * DestroyCursorIcon (X11DRV.@)
797 void CDECL X11DRV_DestroyCursorIcon( HCURSOR handle )
799 Cursor cursor;
801 wine_tsx11_lock();
802 if (cursor_context && !XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&cursor ))
804 TRACE( "%p xid %lx\n", handle, cursor );
805 XFreeCursor( gdi_display, cursor );
806 XDeleteContext( gdi_display, (XID)handle, cursor_context );
808 wine_tsx11_unlock();
811 /***********************************************************************
812 * SetCursor (X11DRV.@)
814 void CDECL X11DRV_SetCursor( HCURSOR handle )
816 if (cursor_window) SendNotifyMessageW( cursor_window, WM_X11DRV_SET_CURSOR, 0, (LPARAM)handle );
819 /***********************************************************************
820 * SetCursorPos (X11DRV.@)
822 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y )
824 Display *display = thread_init_display();
825 POINT pt;
827 TRACE( "warping to (%d,%d)\n", x, y );
829 wine_tsx11_lock();
830 if (cursor_pos.x == x && cursor_pos.y == y)
832 wine_tsx11_unlock();
833 /* We still need to generate WM_MOUSEMOVE */
834 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
835 return TRUE;
838 pt.x = x; pt.y = y;
839 clip_point_to_rect( &cursor_clip, &pt);
840 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
841 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
842 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
843 cursor_pos = pt;
844 wine_tsx11_unlock();
845 return TRUE;
848 /***********************************************************************
849 * GetCursorPos (X11DRV.@)
851 BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos)
853 Display *display = thread_init_display();
854 Window root, child;
855 int rootX, rootY, winX, winY;
856 unsigned int xstate;
858 wine_tsx11_lock();
859 if ((GetTickCount() - last_time_modified > 100) &&
860 XQueryPointer( display, root_window, &root, &child,
861 &rootX, &rootY, &winX, &winY, &xstate ))
863 update_button_state( xstate );
864 winX += virtual_screen_rect.left;
865 winY += virtual_screen_rect.top;
866 TRACE("pointer at (%d,%d)\n", winX, winY );
867 cursor_pos.x = winX;
868 cursor_pos.y = winY;
870 *pos = cursor_pos;
871 wine_tsx11_unlock();
872 return TRUE;
876 /***********************************************************************
877 * ClipCursor (X11DRV.@)
879 * Set the cursor clipping rectangle.
881 BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
883 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
884 cursor_clip = virtual_screen_rect;
886 return TRUE;
889 /***********************************************************************
890 * X11DRV_ButtonPress
892 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
894 XButtonEvent *event = &xev->xbutton;
895 int buttonNum = event->button - 1;
896 WORD wData = 0;
897 POINT pt;
899 if (buttonNum >= NB_BUTTONS) return;
900 if (!hwnd) return;
902 switch (buttonNum)
904 case 3:
905 wData = WHEEL_DELTA;
906 break;
907 case 4:
908 wData = -WHEEL_DELTA;
909 break;
910 case 5:
911 wData = XBUTTON1;
912 break;
913 case 6:
914 wData = XBUTTON2;
915 break;
916 case 7:
917 wData = XBUTTON1;
918 break;
919 case 8:
920 wData = XBUTTON2;
921 break;
924 update_user_time( event->time );
925 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
927 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
928 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
932 /***********************************************************************
933 * X11DRV_ButtonRelease
935 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
937 XButtonEvent *event = &xev->xbutton;
938 int buttonNum = event->button - 1;
939 WORD wData = 0;
940 POINT pt;
942 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
943 if (!hwnd) return;
945 switch (buttonNum)
947 case 5:
948 wData = XBUTTON1;
949 break;
950 case 6:
951 wData = XBUTTON2;
952 break;
953 case 7:
954 wData = XBUTTON1;
955 break;
956 case 8:
957 wData = XBUTTON2;
958 break;
961 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
963 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
964 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
968 /***********************************************************************
969 * X11DRV_MotionNotify
971 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
973 XMotionEvent *event = &xev->xmotion;
974 POINT pt;
976 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
978 if (!hwnd) return;
980 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
982 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
983 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
987 /***********************************************************************
988 * X11DRV_EnterNotify
990 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
992 XCrossingEvent *event = &xev->xcrossing;
993 POINT pt;
995 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
997 if (!hwnd) return;
998 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
999 if (event->window == x11drv_thread_data()->grab_window) return;
1001 /* simulate a mouse motion event */
1002 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1004 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1005 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );