comdlg32: Fix some alignment issues in the Dutch translation.
[wine/hramrach.git] / dlls / winex11.drv / mouse.c
blob5a1a09f42602797f6a8a67c40bbcd2a35d5f48f8
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"
42 #include "wine/winuser16.h"
44 #include "x11drv.h"
45 #include "wine/server.h"
46 #include "wine/library.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
51 /**********************************************************************/
53 #ifndef Button6Mask
54 #define Button6Mask (1<<13)
55 #endif
56 #ifndef Button7Mask
57 #define Button7Mask (1<<14)
58 #endif
60 #define NB_BUTTONS 9 /* Windows can handle 5 buttons and the wheel too */
62 static const UINT button_down_flags[NB_BUTTONS] =
64 MOUSEEVENTF_LEFTDOWN,
65 MOUSEEVENTF_MIDDLEDOWN,
66 MOUSEEVENTF_RIGHTDOWN,
67 MOUSEEVENTF_WHEEL,
68 MOUSEEVENTF_WHEEL,
69 MOUSEEVENTF_XDOWN, /* FIXME: horizontal wheel */
70 MOUSEEVENTF_XDOWN,
71 MOUSEEVENTF_XDOWN,
72 MOUSEEVENTF_XDOWN
75 static const UINT button_up_flags[NB_BUTTONS] =
77 MOUSEEVENTF_LEFTUP,
78 MOUSEEVENTF_MIDDLEUP,
79 MOUSEEVENTF_RIGHTUP,
82 MOUSEEVENTF_XUP,
83 MOUSEEVENTF_XUP,
84 MOUSEEVENTF_XUP,
85 MOUSEEVENTF_XUP
88 POINT cursor_pos;
89 static HWND cursor_window;
90 static DWORD last_time_modified;
91 static RECT cursor_clip; /* Cursor clipping rect */
92 static XContext cursor_context;
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 if (!cursor)
181 XColor bg;
182 Pixmap pixmap;
184 bg.red = bg.green = bg.blue = 0x0000;
185 pixmap = XCreateBitmapFromData( gdi_display, root_window, data, 1, 1 );
186 if (pixmap)
188 cursor = XCreatePixmapCursor( gdi_display, pixmap, pixmap, &bg, &bg, 0, 0 );
189 XFreePixmap( gdi_display, pixmap );
192 return cursor;
195 /***********************************************************************
196 * get_x11_cursor
198 Cursor get_x11_cursor( HCURSOR handle )
200 Cursor cursor;
202 if (!handle) return get_empty_cursor();
204 if (cursor_context && !XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&cursor ))
205 return cursor;
206 return 0;
209 /***********************************************************************
210 * set_window_cursor
212 void set_window_cursor( HWND hwnd, HCURSOR handle )
214 struct x11drv_win_data *data;
215 Cursor cursor;
217 if (!(data = X11DRV_get_win_data( hwnd ))) return;
219 wine_tsx11_lock();
220 if ((cursor = get_x11_cursor( handle )))
222 TRACE( "%p xid %lx\n", handle, cursor );
223 XDefineCursor( gdi_display, data->whole_window, cursor );
224 /* Make the change take effect immediately */
225 XFlush( gdi_display );
226 data->cursor = handle;
228 wine_tsx11_unlock();
231 /***********************************************************************
232 * update_mouse_state
234 * Update the various window states on a mouse event.
236 static void update_mouse_state( HWND hwnd, Window window, int x, int y, unsigned int state, POINT *pt )
238 struct x11drv_thread_data *data = x11drv_thread_data();
240 get_coords( hwnd, window, x, y, pt );
242 cursor_window = hwnd;
244 /* update the wine server Z-order */
246 if (window != data->grab_window &&
247 /* ignore event if a button is pressed, since the mouse is then grabbed too */
248 !(state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask|Button6Mask|Button7Mask)))
250 SERVER_START_REQ( update_window_zorder )
252 req->window = wine_server_user_handle( hwnd );
253 req->rect.left = pt->x;
254 req->rect.top = pt->y;
255 req->rect.right = pt->x + 1;
256 req->rect.bottom = pt->y + 1;
257 wine_server_call( req );
259 SERVER_END_REQ;
264 /***********************************************************************
265 * get_key_state
267 static WORD get_key_state(void)
269 WORD ret = 0;
271 if (GetSystemMetrics( SM_SWAPBUTTON ))
273 if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_LBUTTON;
274 if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_RBUTTON;
276 else
278 if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_LBUTTON;
279 if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_RBUTTON;
281 if (key_state_table[VK_MBUTTON] & 0x80) ret |= MK_MBUTTON;
282 if (key_state_table[VK_SHIFT] & 0x80) ret |= MK_SHIFT;
283 if (key_state_table[VK_CONTROL] & 0x80) ret |= MK_CONTROL;
284 if (key_state_table[VK_XBUTTON1] & 0x80) ret |= MK_XBUTTON1;
285 if (key_state_table[VK_XBUTTON2] & 0x80) ret |= MK_XBUTTON2;
286 return ret;
290 /***********************************************************************
291 * queue_raw_mouse_message
293 static void queue_raw_mouse_message( UINT message, HWND hwnd, DWORD x, DWORD y,
294 DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
296 MSLLHOOKSTRUCT hook;
297 HCURSOR cursor;
299 hook.pt.x = x;
300 hook.pt.y = y;
301 hook.mouseData = MAKELONG( 0, data );
302 hook.flags = injected_flags;
303 hook.time = time;
304 hook.dwExtraInfo = extra_info;
306 last_time_modified = GetTickCount();
307 if (HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE )) return;
309 SERVER_START_REQ( send_hardware_message )
311 req->id = (injected_flags & LLMHF_INJECTED) ? 0 : GetCurrentThreadId();
312 req->win = wine_server_user_handle( hwnd );
313 req->msg = message;
314 req->wparam = MAKEWPARAM( get_key_state(), data );
315 req->lparam = 0;
316 req->x = x;
317 req->y = y;
318 req->time = time;
319 req->info = extra_info;
320 wine_server_call( req );
321 cursor = (reply->count >= 0) ? wine_server_ptr_handle(reply->cursor) : 0;
323 SERVER_END_REQ;
325 if (hwnd)
327 Cursor xcursor;
328 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
329 if (data && cursor != data->cursor)
331 wine_tsx11_lock();
332 if ((xcursor = get_x11_cursor( cursor )))
333 XDefineCursor( gdi_display, data->whole_window, xcursor );
334 data->cursor = cursor;
335 wine_tsx11_unlock();
341 /***********************************************************************
342 * X11DRV_send_mouse_input
344 void X11DRV_send_mouse_input( HWND hwnd, DWORD flags, DWORD x, DWORD y,
345 DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
347 POINT pt;
349 if (flags & MOUSEEVENTF_MOVE && flags & MOUSEEVENTF_ABSOLUTE)
351 if (injected_flags & LLMHF_INJECTED)
353 pt.x = (x * screen_width) >> 16;
354 pt.y = (y * screen_height) >> 16;
356 else
358 pt.x = x;
359 pt.y = y;
360 wine_tsx11_lock();
361 if (cursor_pos.x == x && cursor_pos.y == y &&
362 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
363 flags &= ~MOUSEEVENTF_MOVE;
364 wine_tsx11_unlock();
367 else if (flags & MOUSEEVENTF_MOVE)
369 int accel[3], xMult = 1, yMult = 1;
371 /* dx and dy can be negative numbers for relative movements */
372 SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
374 if (abs(x) > accel[0] && accel[2] != 0)
376 xMult = 2;
377 if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
379 if (abs(y) > accel[0] && accel[2] != 0)
381 yMult = 2;
382 if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
385 wine_tsx11_lock();
386 pt.x = cursor_pos.x + (long)x * xMult;
387 pt.y = cursor_pos.y + (long)y * yMult;
388 wine_tsx11_unlock();
390 else
392 wine_tsx11_lock();
393 pt = cursor_pos;
394 wine_tsx11_unlock();
397 if (flags & MOUSEEVENTF_MOVE)
399 queue_raw_mouse_message( WM_MOUSEMOVE, hwnd, pt.x, pt.y, data, time,
400 extra_info, injected_flags );
401 if ((injected_flags & LLMHF_INJECTED) &&
402 ((flags & MOUSEEVENTF_ABSOLUTE) || x || y)) /* we have to actually move the cursor */
404 X11DRV_SetCursorPos( pt.x, pt.y );
406 else
408 wine_tsx11_lock();
409 clip_point_to_rect( &cursor_clip, &pt);
410 cursor_pos = pt;
411 wine_tsx11_unlock();
414 if (flags & MOUSEEVENTF_LEFTDOWN)
416 key_state_table[VK_LBUTTON] |= 0xc0;
417 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
418 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
420 if (flags & MOUSEEVENTF_LEFTUP)
422 key_state_table[VK_LBUTTON] &= ~0x80;
423 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
424 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
426 if (flags & MOUSEEVENTF_RIGHTDOWN)
428 key_state_table[VK_RBUTTON] |= 0xc0;
429 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
430 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
432 if (flags & MOUSEEVENTF_RIGHTUP)
434 key_state_table[VK_RBUTTON] &= ~0x80;
435 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
436 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
438 if (flags & MOUSEEVENTF_MIDDLEDOWN)
440 key_state_table[VK_MBUTTON] |= 0xc0;
441 queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
442 extra_info, injected_flags );
444 if (flags & MOUSEEVENTF_MIDDLEUP)
446 key_state_table[VK_MBUTTON] &= ~0x80;
447 queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
448 extra_info, injected_flags );
450 if (flags & MOUSEEVENTF_WHEEL)
452 queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
453 extra_info, injected_flags );
455 if (flags & MOUSEEVENTF_XDOWN)
457 key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
458 queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
459 extra_info, injected_flags );
461 if (flags & MOUSEEVENTF_XUP)
463 key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
464 queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
465 extra_info, injected_flags );
470 /***********************************************************************
471 * check_alpha_zero
473 * Generally 32 bit bitmaps have an alpha channel which is used in favor of the
474 * AND mask. However, if all pixels have alpha = 0x00, the bitmap is treated
475 * like one without alpha and the masks are used. As soon as one pixel has
476 * alpha != 0x00, and the mask ignored as described in the docs.
478 * This is most likely for applications which create the bitmaps with
479 * CreateDIBitmap, which creates a device dependent bitmap, so the format that
480 * arrives when loading depends on the screen's bpp. Apps that were written at
481 * 8 / 16 bpp times do not know about the 32 bit alpha, so they would get a
482 * completely transparent cursor on 32 bit displays.
484 * Non-32 bit bitmaps always use the AND mask.
486 static BOOL check_alpha_zero(CURSORICONINFO *ptr, unsigned char *xor_bits)
488 int x, y;
489 unsigned char *xor_ptr;
491 if (ptr->bBitsPerPixel == 32)
493 for (y = 0; y < ptr->nHeight; ++y)
495 xor_ptr = xor_bits + (y * ptr->nWidthBytes);
496 for (x = 0; x < ptr->nWidth; ++x)
498 if (xor_ptr[3] != 0x00)
500 return FALSE;
502 xor_ptr+=4;
507 return TRUE;
511 #ifdef SONAME_LIBXCURSOR
513 /***********************************************************************
514 * create_cursor_image
516 * Create an XcursorImage from a CURSORICONINFO
518 static XcursorImage *create_cursor_image( CURSORICONINFO *ptr )
520 static const unsigned char convert_5to8[] =
522 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
523 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
524 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
525 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
527 static const unsigned char convert_6to8[] =
529 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
530 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
531 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
532 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
533 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
534 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
535 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
536 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
538 int x;
539 int y;
540 int and_size;
541 unsigned char *and_bits, *and_ptr, *xor_bits, *xor_ptr;
542 int and_width_bytes, xor_width_bytes;
543 XcursorPixel *pixel_ptr;
544 XcursorImage *image;
545 unsigned char tmp;
546 BOOL alpha_zero;
548 and_width_bytes = 2 * ((ptr->nWidth+15) / 16);
549 xor_width_bytes = ptr->nWidthBytes;
551 and_size = ptr->nHeight * and_width_bytes;
552 and_ptr = and_bits = (unsigned char *)(ptr + 1);
554 xor_ptr = xor_bits = and_ptr + and_size;
556 image = pXcursorImageCreate( ptr->nWidth, ptr->nHeight );
557 if (!image) return NULL;
559 pixel_ptr = image->pixels;
561 alpha_zero = check_alpha_zero(ptr, xor_bits);
563 /* On windows, to calculate the color for a pixel, first an AND is done
564 * with the background and the "and" bitmap, then an XOR with the "xor"
565 * bitmap. This means that when the data in the "and" bitmap is 0, the
566 * pixel will get the color as specified in the "xor" bitmap.
567 * However, if the data in the "and" bitmap is 1, the result will be the
568 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
569 * data is completely black (0x000000) the pixel will become transparent,
570 * in case it's white (0xffffff) the pixel will become the inverse of the
571 * background color.
573 * Since we can't support inverting colors, we map the grayscale value of
574 * the "xor" data to the alpha channel, and xor the color with either
575 * black or white.
577 for (y = 0; y < ptr->nHeight; ++y)
579 and_ptr = and_bits + (y * and_width_bytes);
580 xor_ptr = xor_bits + (y * xor_width_bytes);
582 for (x = 0; x < ptr->nWidth; ++x)
584 /* Xcursor pixel data is in ARGB format, with A in the high byte */
585 switch (ptr->bBitsPerPixel)
587 case 32:
588 /* BGRA, 8 bits each */
589 *pixel_ptr = *xor_ptr++;
590 *pixel_ptr |= *xor_ptr++ << 8;
591 *pixel_ptr |= *xor_ptr++ << 16;
592 *pixel_ptr |= *xor_ptr++ << 24;
593 break;
595 case 24:
596 /* BGR, 8 bits each */
597 *pixel_ptr = *xor_ptr++;
598 *pixel_ptr |= *xor_ptr++ << 8;
599 *pixel_ptr |= *xor_ptr++ << 16;
600 break;
602 case 16:
603 /* BGR, 5 red, 6 green, 5 blue */
604 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
605 *pixel_ptr = convert_5to8[*xor_ptr & 0x1f];
606 tmp = (*xor_ptr++ & 0xe0) >> 5;
607 tmp |= (*xor_ptr & 0x07) << 3;
608 *pixel_ptr |= convert_6to8[tmp] << 16;
609 *pixel_ptr |= convert_5to8[*xor_ptr++ >> 3] << 24;
610 break;
612 case 1:
613 if (*xor_ptr & (1 << (7 - (x & 7)))) *pixel_ptr = 0xffffff;
614 else *pixel_ptr = 0;
615 if ((x & 7) == 7) ++xor_ptr;
616 break;
618 default:
619 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr->bBitsPerPixel);
620 return 0;
623 if (alpha_zero)
625 /* Alpha channel */
626 if (~*and_ptr & (1 << (7 - (x & 7)))) *pixel_ptr |= 0xff << 24;
627 else if (*pixel_ptr)
629 int alpha = (*pixel_ptr & 0xff) * 0.30f
630 + ((*pixel_ptr & 0xff00) >> 8) * 0.55f
631 + ((*pixel_ptr & 0xff0000) >> 16) * 0.15f;
632 *pixel_ptr ^= ((x + y) % 2) ? 0xffffff : 0x000000;
633 *pixel_ptr |= alpha << 24;
635 if ((x & 7) == 7) ++and_ptr;
637 ++pixel_ptr;
641 return image;
645 /***********************************************************************
646 * create_xcursor_cursor
648 * Use Xcursor to create an X cursor from a Windows one.
650 static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
652 Cursor cursor;
653 XcursorImage *image;
655 image = create_cursor_image( ptr );
656 if (!image) return 0;
658 /* Make sure hotspot is valid */
659 image->xhot = ptr->ptHotSpot.x;
660 image->yhot = ptr->ptHotSpot.y;
661 if (image->xhot >= image->width ||
662 image->yhot >= image->height)
664 image->xhot = image->width / 2;
665 image->yhot = image->height / 2;
668 image->delay = 0;
670 cursor = pXcursorImageLoadCursor( display, image );
671 pXcursorImageDestroy( image );
673 return cursor;
676 #endif /* SONAME_LIBXCURSOR */
679 /***********************************************************************
680 * create_cursor
682 * Create an X cursor from a Windows one.
684 static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
686 Pixmap pixmapBits, pixmapMask, pixmapMaskInv = 0, pixmapAll;
687 XColor fg, bg;
688 Cursor cursor = None;
689 POINT hotspot;
690 char *bitMask32 = NULL;
691 BOOL alpha_zero = TRUE;
693 if (!ptr) return get_empty_cursor();
695 #ifdef SONAME_LIBXCURSOR
696 if (pXcursorImageLoadCursor) return create_xcursor_cursor( display, ptr );
697 #endif
699 /* Create the X cursor from the bits */
701 XImage *image;
702 GC gc;
704 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
705 ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
706 ptr->nWidthBytes);
708 /* Create a pixmap and transfer all the bits to it */
710 /* NOTE: Following hack works, but only because XFree depth
711 * 1 images really use 1 bit/pixel (and so the same layout
712 * as the Windows cursor data). Perhaps use a more generic
713 * algorithm here.
715 /* This pixmap will be written with two bitmaps. The first is
716 * the mask and the second is the image.
718 if (!(pixmapAll = XCreatePixmap( display, root_window,
719 ptr->nWidth, ptr->nHeight * 2, 1 )))
720 return 0;
721 if (!(image = XCreateImage( display, visual,
722 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
723 ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
725 XFreePixmap( display, pixmapAll );
726 return 0;
728 gc = XCreateGC( display, pixmapAll, 0, NULL );
729 XSetGraphicsExposures( display, gc, False );
730 image->byte_order = MSBFirst;
731 image->bitmap_bit_order = MSBFirst;
732 image->bitmap_unit = 16;
733 _XInitImageFuncPtrs(image);
734 if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
736 /* A plain old white on black cursor. */
737 fg.red = fg.green = fg.blue = 0xffff;
738 bg.red = bg.green = bg.blue = 0x0000;
739 XPutImage( display, pixmapAll, gc, image,
740 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
742 else
744 int rbits, gbits, bbits, red, green, blue;
745 int rfg, gfg, bfg, rbg, gbg, bbg;
746 int rscale, gscale, bscale;
747 int x, y, xmax, ymax, byteIndex, xorIndex;
748 unsigned char *theMask, *theImage, theChar;
749 int threshold, fgBits, bgBits, bitShifted;
750 BYTE pXorBits[128]; /* Up to 32x32 icons */
752 switch (ptr->bBitsPerPixel)
754 case 32:
755 bitMask32 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
756 ptr->nWidth * ptr->nHeight / 8 );
757 /* Fallthrough */
758 case 24:
759 rbits = 8;
760 gbits = 8;
761 bbits = 8;
762 threshold = 0x40;
763 break;
764 case 16:
765 rbits = 5;
766 gbits = 6;
767 bbits = 5;
768 threshold = 0x40;
769 break;
770 default:
771 FIXME("Currently no support for cursors with %d bits per pixel\n",
772 ptr->bBitsPerPixel);
773 XFreePixmap( display, pixmapAll );
774 XFreeGC( display, gc );
775 image->data = NULL;
776 XDestroyImage( image );
777 return 0;
779 /* The location of the mask. */
780 theMask = (unsigned char *)(ptr + 1);
781 /* The mask should still be 1 bit per pixel. The color image
782 * should immediately follow the mask.
784 theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
785 rfg = gfg = bfg = rbg = gbg = bbg = 0;
786 byteIndex = 0;
787 xorIndex = 0;
788 fgBits = 0;
789 bitShifted = 0x01;
790 xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
791 if (ptr->nWidth > 32) {
792 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
793 ptr->nWidth, ptr->nHeight);
795 ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
796 alpha_zero = check_alpha_zero(ptr, theImage);
798 memset(pXorBits, 0, 128);
799 for (y=0; y<ymax; y++)
801 for (x=0; x<xmax; x++)
803 red = green = blue = 0;
804 switch (ptr->bBitsPerPixel)
806 case 32:
807 theChar = theImage[byteIndex++];
808 blue = theChar;
809 theChar = theImage[byteIndex++];
810 green = theChar;
811 theChar = theImage[byteIndex++];
812 red = theChar;
813 theChar = theImage[byteIndex++];
814 /* If the alpha channel is >5% transparent,
815 * assume that we can add it to the bitMask32.
817 if (theChar > 0x0D)
818 *(bitMask32 + (y*xmax+x)/8) |= 1 << (x & 7);
819 break;
820 case 24:
821 theChar = theImage[byteIndex++];
822 blue = theChar;
823 theChar = theImage[byteIndex++];
824 green = theChar;
825 theChar = theImage[byteIndex++];
826 red = theChar;
827 break;
828 case 16:
829 theChar = theImage[byteIndex++];
830 blue = theChar & 0x1F;
831 green = (theChar & 0xE0) >> 5;
832 theChar = theImage[byteIndex++];
833 green |= (theChar & 0x07) << 3;
834 red = (theChar & 0xF8) >> 3;
835 break;
838 if (red+green+blue > threshold)
840 rfg += red;
841 gfg += green;
842 bfg += blue;
843 fgBits++;
844 pXorBits[xorIndex] |= bitShifted;
846 else
848 rbg += red;
849 gbg += green;
850 bbg += blue;
852 if (x%8 == 7)
854 bitShifted = 0x01;
855 xorIndex++;
857 else
858 bitShifted = bitShifted << 1;
861 rscale = 1 << (16 - rbits);
862 gscale = 1 << (16 - gbits);
863 bscale = 1 << (16 - bbits);
864 if (fgBits)
866 fg.red = rfg * rscale / fgBits;
867 fg.green = gfg * gscale / fgBits;
868 fg.blue = bfg * bscale / fgBits;
870 else fg.red = fg.green = fg.blue = 0;
871 bgBits = xmax * ymax - fgBits;
872 if (bgBits)
874 bg.red = rbg * rscale / bgBits;
875 bg.green = gbg * gscale / bgBits;
876 bg.blue = bbg * bscale / bgBits;
878 else bg.red = bg.green = bg.blue = 0;
879 pixmapBits = XCreateBitmapFromData( display, root_window, (char *)pXorBits, xmax, ymax );
880 if (!pixmapBits)
882 HeapFree( GetProcessHeap(), 0, bitMask32 );
883 XFreePixmap( display, pixmapAll );
884 XFreeGC( display, gc );
885 image->data = NULL;
886 XDestroyImage( image );
887 return 0;
890 /* Put the mask. */
891 XPutImage( display, pixmapAll, gc, image,
892 0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
893 XSetFunction( display, gc, GXcopy );
894 /* Put the image */
895 XCopyArea( display, pixmapBits, pixmapAll, gc,
896 0, 0, xmax, ymax, 0, ptr->nHeight );
897 XFreePixmap( display, pixmapBits );
899 image->data = NULL;
900 XDestroyImage( image );
902 /* Now create the 2 pixmaps for bits and mask */
904 pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
905 if (alpha_zero)
907 pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
908 pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
910 /* Make sure everything went OK so far */
911 if (pixmapBits && pixmapMask && pixmapMaskInv)
913 /* We have to do some magic here, as cursors are not fully
914 * compatible between Windows and X11. Under X11, there are
915 * only 3 possible color cursor: black, white and masked. So
916 * we map the 4th Windows color (invert the bits on the screen)
917 * to black and an additional white bit on an other place
918 * (+1,+1). This require some boolean arithmetic:
920 * Windows | X11
921 * And Xor Result | Bits Mask Result
922 * 0 0 black | 0 1 background
923 * 0 1 white | 1 1 foreground
924 * 1 0 no change | X 0 no change
925 * 1 1 inverted | 0 1 background
927 * which gives:
928 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
929 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
931 * FIXME: apparently some servers do support 'inverted' color.
932 * I don't know if it's correct per the X spec, but maybe we
933 * ought to take advantage of it. -- AJ
935 XSetFunction( display, gc, GXcopy );
936 XCopyArea( display, pixmapAll, pixmapBits, gc,
937 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
938 XCopyArea( display, pixmapAll, pixmapMask, gc,
939 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
940 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
941 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
942 XSetFunction( display, gc, GXand );
943 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
944 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
945 XSetFunction( display, gc, GXandReverse );
946 XCopyArea( display, pixmapAll, pixmapBits, gc,
947 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
948 XSetFunction( display, gc, GXorReverse );
949 XCopyArea( display, pixmapAll, pixmapMask, gc,
950 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
951 /* Additional white */
952 XSetFunction( display, gc, GXor );
953 XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
954 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
955 XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
956 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
957 XSetFunction( display, gc, GXcopy );
960 else
962 pixmapMask = XCreateBitmapFromData( display, root_window,
963 bitMask32, ptr->nWidth,
964 ptr->nHeight );
967 /* Make sure hotspot is valid */
968 hotspot.x = ptr->ptHotSpot.x;
969 hotspot.y = ptr->ptHotSpot.y;
970 if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
971 hotspot.y < 0 || hotspot.y >= ptr->nHeight)
973 hotspot.x = ptr->nWidth / 2;
974 hotspot.y = ptr->nHeight / 2;
977 if (pixmapBits && pixmapMask)
978 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
979 &fg, &bg, hotspot.x, hotspot.y );
981 /* Now free everything */
983 if (pixmapAll) XFreePixmap( display, pixmapAll );
984 if (pixmapBits) XFreePixmap( display, pixmapBits );
985 if (pixmapMask) XFreePixmap( display, pixmapMask );
986 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
987 HeapFree( GetProcessHeap(), 0, bitMask32 );
988 XFreeGC( display, gc );
990 return cursor;
993 /***********************************************************************
994 * CreateCursorIcon (X11DRV.@)
996 void CDECL X11DRV_CreateCursorIcon( HCURSOR handle, CURSORICONINFO *info )
998 static const WORD ICON_HOTSPOT = 0x4242;
999 Cursor cursor;
1001 /* ignore icons (FIXME: shouldn't use magic hotspot value) */
1002 if (info->ptHotSpot.x == ICON_HOTSPOT && info->ptHotSpot.y == ICON_HOTSPOT) return;
1004 wine_tsx11_lock();
1005 cursor = create_cursor( gdi_display, info );
1006 if (cursor)
1008 if (!cursor_context) cursor_context = XUniqueContext();
1009 XSaveContext( gdi_display, (XID)handle, cursor_context, (char *)cursor );
1010 TRACE( "cursor %p %ux%u, planes %u, bpp %u -> xid %lx\n",
1011 handle, info->nWidth, info->nHeight, info->bPlanes, info->bBitsPerPixel, cursor );
1013 wine_tsx11_unlock();
1016 /***********************************************************************
1017 * DestroyCursorIcon (X11DRV.@)
1019 void CDECL X11DRV_DestroyCursorIcon( HCURSOR handle )
1021 Cursor cursor;
1023 wine_tsx11_lock();
1024 if ((cursor = get_x11_cursor( handle )))
1026 TRACE( "%p xid %lx\n", handle, cursor );
1027 XFreeCursor( gdi_display, cursor );
1028 XDeleteContext( gdi_display, (XID)handle, cursor_context );
1030 wine_tsx11_unlock();
1033 /***********************************************************************
1034 * SetCursor (X11DRV.@)
1036 void CDECL X11DRV_SetCursor( HCURSOR handle )
1038 if (cursor_window) SendNotifyMessageW( cursor_window, WM_X11DRV_SET_CURSOR, 0, (LPARAM)handle );
1041 /***********************************************************************
1042 * SetCursorPos (X11DRV.@)
1044 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y )
1046 Display *display = thread_init_display();
1047 POINT pt;
1049 TRACE( "warping to (%d,%d)\n", x, y );
1051 wine_tsx11_lock();
1052 if (cursor_pos.x == x && cursor_pos.y == y)
1054 wine_tsx11_unlock();
1055 /* We still need to generate WM_MOUSEMOVE */
1056 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
1057 return TRUE;
1060 pt.x = x; pt.y = y;
1061 clip_point_to_rect( &cursor_clip, &pt);
1062 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
1063 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
1064 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
1065 cursor_pos = pt;
1066 wine_tsx11_unlock();
1067 return TRUE;
1070 /***********************************************************************
1071 * GetCursorPos (X11DRV.@)
1073 BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos)
1075 Display *display = thread_init_display();
1076 Window root, child;
1077 int rootX, rootY, winX, winY;
1078 unsigned int xstate;
1080 wine_tsx11_lock();
1081 if ((GetTickCount() - last_time_modified > 100) &&
1082 XQueryPointer( display, root_window, &root, &child,
1083 &rootX, &rootY, &winX, &winY, &xstate ))
1085 update_button_state( xstate );
1086 winX += virtual_screen_rect.left;
1087 winY += virtual_screen_rect.top;
1088 TRACE("pointer at (%d,%d)\n", winX, winY );
1089 cursor_pos.x = winX;
1090 cursor_pos.y = winY;
1092 *pos = cursor_pos;
1093 wine_tsx11_unlock();
1094 return TRUE;
1098 /***********************************************************************
1099 * ClipCursor (X11DRV.@)
1101 * Set the cursor clipping rectangle.
1103 BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
1105 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
1106 cursor_clip = virtual_screen_rect;
1108 return TRUE;
1111 /***********************************************************************
1112 * X11DRV_ButtonPress
1114 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
1116 XButtonEvent *event = &xev->xbutton;
1117 int buttonNum = event->button - 1;
1118 WORD wData = 0;
1119 POINT pt;
1121 if (buttonNum >= NB_BUTTONS) return;
1122 if (!hwnd) return;
1124 switch (buttonNum)
1126 case 3:
1127 wData = WHEEL_DELTA;
1128 break;
1129 case 4:
1130 wData = -WHEEL_DELTA;
1131 break;
1132 case 5:
1133 wData = XBUTTON1;
1134 break;
1135 case 6:
1136 wData = XBUTTON2;
1137 break;
1138 case 7:
1139 wData = XBUTTON1;
1140 break;
1141 case 8:
1142 wData = XBUTTON2;
1143 break;
1146 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1148 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1149 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1153 /***********************************************************************
1154 * X11DRV_ButtonRelease
1156 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
1158 XButtonEvent *event = &xev->xbutton;
1159 int buttonNum = event->button - 1;
1160 WORD wData = 0;
1161 POINT pt;
1163 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
1164 if (!hwnd) return;
1166 switch (buttonNum)
1168 case 5:
1169 wData = XBUTTON1;
1170 break;
1171 case 6:
1172 wData = XBUTTON2;
1173 break;
1174 case 7:
1175 wData = XBUTTON1;
1176 break;
1177 case 8:
1178 wData = XBUTTON2;
1179 break;
1182 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1184 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1185 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1189 /***********************************************************************
1190 * X11DRV_MotionNotify
1192 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
1194 XMotionEvent *event = &xev->xmotion;
1195 POINT pt;
1197 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
1199 if (!hwnd) return;
1201 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1203 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1204 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1208 /***********************************************************************
1209 * X11DRV_EnterNotify
1211 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
1213 XCrossingEvent *event = &xev->xcrossing;
1214 POINT pt;
1216 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
1218 if (!hwnd) return;
1219 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
1220 if (event->window == x11drv_thread_data()->grab_window) return;
1222 /* simulate a mouse motion event */
1223 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1225 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1226 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );