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
23 #include "wine/port.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);
36 #endif /* SONAME_LIBXCURSOR */
38 #define NONAMELESSUNION
39 #define NONAMELESSSTRUCT
42 #include "wine/winuser16.h"
45 #include "wine/server.h"
46 #include "wine/library.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
51 /**********************************************************************/
54 #define Button6Mask (1<<13)
57 #define Button7Mask (1<<14)
60 #define NB_BUTTONS 9 /* Windows can handle 5 buttons and the wheel too */
62 static const UINT button_down_flags[NB_BUTTONS] =
65 MOUSEEVENTF_MIDDLEDOWN,
66 MOUSEEVENTF_RIGHTDOWN,
69 MOUSEEVENTF_XDOWN, /* FIXME: horizontal wheel */
75 static const UINT button_up_flags[NB_BUTTONS] =
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 /***********************************************************************
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");
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);
118 #endif /* SONAME_LIBXCURSOR */
122 /***********************************************************************
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 );
133 if (window == data->client_window)
135 pt->x = x + data->client_rect.left;
136 pt->y = y + data->client_rect.top;
140 pt->x = x + data->whole_rect.left;
141 pt->y = y + data->whole_rect.top;
145 /***********************************************************************
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 /***********************************************************************
174 static Cursor get_empty_cursor(void)
176 static Cursor cursor;
177 static const char data[] = { 0 };
184 bg.red = bg.green = bg.blue = 0x0000;
185 pixmap = XCreateBitmapFromData( gdi_display, root_window, data, 1, 1 );
188 cursor = XCreatePixmapCursor( gdi_display, pixmap, pixmap, &bg, &bg, 0, 0 );
189 XFreePixmap( gdi_display, pixmap );
195 /***********************************************************************
198 Cursor get_x11_cursor( HCURSOR handle )
202 if (!handle) return get_empty_cursor();
204 if (cursor_context && !XFindContext( gdi_display, (XID)handle, cursor_context, (char **)&cursor ))
209 /***********************************************************************
212 void set_window_cursor( HWND hwnd, HCURSOR handle )
214 struct x11drv_win_data *data;
217 if (!(data = X11DRV_get_win_data( hwnd ))) return;
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;
231 /***********************************************************************
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 );
264 /***********************************************************************
267 static WORD get_key_state(void)
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;
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;
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 )
301 hook.mouseData = MAKELONG( 0, data );
302 hook.flags = injected_flags;
304 hook.dwExtraInfo = extra_info;
306 last_time_modified = GetTickCount();
307 if (HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE ))
308 message = 0; /* ignore it */
310 SERVER_START_REQ( send_hardware_message )
312 req->id = (injected_flags & LLMHF_INJECTED) ? 0 : GetCurrentThreadId();
313 req->win = wine_server_user_handle( hwnd );
315 req->wparam = MAKEWPARAM( get_key_state(), data );
320 req->info = extra_info;
321 wine_server_call( req );
322 cursor = (reply->count >= 0) ? wine_server_ptr_handle(reply->cursor) : 0;
329 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
330 if (data && cursor != data->cursor)
333 if ((xcursor = get_x11_cursor( cursor )))
334 XDefineCursor( gdi_display, data->whole_window, xcursor );
335 data->cursor = cursor;
342 /***********************************************************************
343 * X11DRV_send_mouse_input
345 void X11DRV_send_mouse_input( HWND hwnd, DWORD flags, DWORD x, DWORD y,
346 DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
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;
362 if (cursor_pos.x == x && cursor_pos.y == y &&
363 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
364 flags &= ~MOUSEEVENTF_MOVE;
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)
378 if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
380 if (abs(y) > accel[0] && accel[2] != 0)
383 if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
387 pt.x = cursor_pos.x + (long)x * xMult;
388 pt.y = cursor_pos.y + (long)y * yMult;
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 );
410 clip_point_to_rect( &cursor_clip, &pt);
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 );
471 /***********************************************************************
474 * Generally 32 bit bitmaps have an alpha channel which is used in favor of the
475 * AND mask. However, if all pixels have alpha = 0x00, the bitmap is treated
476 * like one without alpha and the masks are used. As soon as one pixel has
477 * alpha != 0x00, and the mask ignored as described in the docs.
479 * This is most likely for applications which create the bitmaps with
480 * CreateDIBitmap, which creates a device dependent bitmap, so the format that
481 * arrives when loading depends on the screen's bpp. Apps that were written at
482 * 8 / 16 bpp times do not know about the 32 bit alpha, so they would get a
483 * completely transparent cursor on 32 bit displays.
485 * Non-32 bit bitmaps always use the AND mask.
487 static BOOL check_alpha_zero(CURSORICONINFO *ptr, unsigned char *xor_bits)
490 unsigned char *xor_ptr;
492 if (ptr->bBitsPerPixel == 32)
494 for (y = 0; y < ptr->nHeight; ++y)
496 xor_ptr = xor_bits + (y * ptr->nWidthBytes);
497 for (x = 0; x < ptr->nWidth; ++x)
499 if (xor_ptr[3] != 0x00)
512 #ifdef SONAME_LIBXCURSOR
514 /***********************************************************************
515 * create_cursor_image
517 * Create an XcursorImage from a CURSORICONINFO
519 static XcursorImage *create_cursor_image( CURSORICONINFO *ptr )
521 static const unsigned char convert_5to8[] =
523 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
524 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
525 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
526 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
528 static const unsigned char convert_6to8[] =
530 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
531 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
532 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
533 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
534 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
535 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
536 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
537 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
542 unsigned char *and_bits, *and_ptr, *xor_bits, *xor_ptr;
543 int and_width_bytes, xor_width_bytes;
544 XcursorPixel *pixel_ptr;
549 and_width_bytes = 2 * ((ptr->nWidth+15) / 16);
550 xor_width_bytes = ptr->nWidthBytes;
552 and_size = ptr->nHeight * and_width_bytes;
553 and_ptr = and_bits = (unsigned char *)(ptr + 1);
555 xor_ptr = xor_bits = and_ptr + and_size;
557 image = pXcursorImageCreate( ptr->nWidth, ptr->nHeight );
558 if (!image) return NULL;
560 pixel_ptr = image->pixels;
562 alpha_zero = check_alpha_zero(ptr, xor_bits);
564 /* On windows, to calculate the color for a pixel, first an AND is done
565 * with the background and the "and" bitmap, then an XOR with the "xor"
566 * bitmap. This means that when the data in the "and" bitmap is 0, the
567 * pixel will get the color as specified in the "xor" bitmap.
568 * However, if the data in the "and" bitmap is 1, the result will be the
569 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
570 * data is completely black (0x000000) the pixel will become transparent,
571 * in case it's white (0xffffff) the pixel will become the inverse of the
574 * Since we can't support inverting colors, we map the grayscale value of
575 * the "xor" data to the alpha channel, and xor the color with either
578 for (y = 0; y < ptr->nHeight; ++y)
580 and_ptr = and_bits + (y * and_width_bytes);
581 xor_ptr = xor_bits + (y * xor_width_bytes);
583 for (x = 0; x < ptr->nWidth; ++x)
585 /* Xcursor pixel data is in ARGB format, with A in the high byte */
586 switch (ptr->bBitsPerPixel)
589 /* BGRA, 8 bits each */
590 *pixel_ptr = *xor_ptr++;
591 *pixel_ptr |= *xor_ptr++ << 8;
592 *pixel_ptr |= *xor_ptr++ << 16;
593 *pixel_ptr |= *xor_ptr++ << 24;
597 /* BGR, 8 bits each */
598 *pixel_ptr = *xor_ptr++;
599 *pixel_ptr |= *xor_ptr++ << 8;
600 *pixel_ptr |= *xor_ptr++ << 16;
604 /* BGR, 5 red, 6 green, 5 blue */
605 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
606 *pixel_ptr = convert_5to8[*xor_ptr & 0x1f];
607 tmp = (*xor_ptr++ & 0xe0) >> 5;
608 tmp |= (*xor_ptr & 0x07) << 3;
609 *pixel_ptr |= convert_6to8[tmp] << 16;
610 *pixel_ptr |= convert_5to8[*xor_ptr++ >> 3] << 24;
614 if (*xor_ptr & (1 << (7 - (x & 7)))) *pixel_ptr = 0xffffff;
616 if ((x & 7) == 7) ++xor_ptr;
620 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr->bBitsPerPixel);
627 if (~*and_ptr & (1 << (7 - (x & 7)))) *pixel_ptr |= 0xff << 24;
630 int alpha = (*pixel_ptr & 0xff) * 0.30f
631 + ((*pixel_ptr & 0xff00) >> 8) * 0.55f
632 + ((*pixel_ptr & 0xff0000) >> 16) * 0.15f;
633 *pixel_ptr ^= ((x + y) % 2) ? 0xffffff : 0x000000;
634 *pixel_ptr |= alpha << 24;
636 if ((x & 7) == 7) ++and_ptr;
646 /***********************************************************************
647 * create_xcursor_cursor
649 * Use Xcursor to create an X cursor from a Windows one.
651 static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
656 image = create_cursor_image( ptr );
657 if (!image) return 0;
659 /* Make sure hotspot is valid */
660 image->xhot = ptr->ptHotSpot.x;
661 image->yhot = ptr->ptHotSpot.y;
662 if (image->xhot >= image->width ||
663 image->yhot >= image->height)
665 image->xhot = image->width / 2;
666 image->yhot = image->height / 2;
671 cursor = pXcursorImageLoadCursor( display, image );
672 pXcursorImageDestroy( image );
677 #endif /* SONAME_LIBXCURSOR */
680 /***********************************************************************
683 * Create an X cursor from a Windows one.
685 static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
687 Pixmap pixmapBits, pixmapMask, pixmapMaskInv = 0, pixmapAll;
689 Cursor cursor = None;
691 char *bitMask32 = NULL;
692 BOOL alpha_zero = TRUE;
694 if (!ptr) return get_empty_cursor();
696 #ifdef SONAME_LIBXCURSOR
697 if (pXcursorImageLoadCursor) return create_xcursor_cursor( display, ptr );
700 /* Create the X cursor from the bits */
705 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
706 ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
709 /* Create a pixmap and transfer all the bits to it */
711 /* NOTE: Following hack works, but only because XFree depth
712 * 1 images really use 1 bit/pixel (and so the same layout
713 * as the Windows cursor data). Perhaps use a more generic
716 /* This pixmap will be written with two bitmaps. The first is
717 * the mask and the second is the image.
719 if (!(pixmapAll = XCreatePixmap( display, root_window,
720 ptr->nWidth, ptr->nHeight * 2, 1 )))
722 if (!(image = XCreateImage( display, visual,
723 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
724 ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
726 XFreePixmap( display, pixmapAll );
729 gc = XCreateGC( display, pixmapAll, 0, NULL );
730 XSetGraphicsExposures( display, gc, False );
731 image->byte_order = MSBFirst;
732 image->bitmap_bit_order = MSBFirst;
733 image->bitmap_unit = 16;
734 _XInitImageFuncPtrs(image);
735 if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
737 /* A plain old white on black cursor. */
738 fg.red = fg.green = fg.blue = 0xffff;
739 bg.red = bg.green = bg.blue = 0x0000;
740 XPutImage( display, pixmapAll, gc, image,
741 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
745 int rbits, gbits, bbits, red, green, blue;
746 int rfg, gfg, bfg, rbg, gbg, bbg;
747 int rscale, gscale, bscale;
748 int x, y, xmax, ymax, byteIndex, xorIndex;
749 unsigned char *theMask, *theImage, theChar;
750 int threshold, fgBits, bgBits, bitShifted;
751 BYTE pXorBits[128]; /* Up to 32x32 icons */
753 switch (ptr->bBitsPerPixel)
756 bitMask32 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
757 ptr->nWidth * ptr->nHeight / 8 );
772 FIXME("Currently no support for cursors with %d bits per pixel\n",
774 XFreePixmap( display, pixmapAll );
775 XFreeGC( display, gc );
777 XDestroyImage( image );
780 /* The location of the mask. */
781 theMask = (unsigned char *)(ptr + 1);
782 /* The mask should still be 1 bit per pixel. The color image
783 * should immediately follow the mask.
785 theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
786 rfg = gfg = bfg = rbg = gbg = bbg = 0;
791 xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
792 if (ptr->nWidth > 32) {
793 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
794 ptr->nWidth, ptr->nHeight);
796 ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
797 alpha_zero = check_alpha_zero(ptr, theImage);
799 memset(pXorBits, 0, 128);
800 for (y=0; y<ymax; y++)
802 for (x=0; x<xmax; x++)
804 red = green = blue = 0;
805 switch (ptr->bBitsPerPixel)
808 theChar = theImage[byteIndex++];
810 theChar = theImage[byteIndex++];
812 theChar = theImage[byteIndex++];
814 theChar = theImage[byteIndex++];
815 /* If the alpha channel is >5% transparent,
816 * assume that we can add it to the bitMask32.
819 *(bitMask32 + (y*xmax+x)/8) |= 1 << (x & 7);
822 theChar = theImage[byteIndex++];
824 theChar = theImage[byteIndex++];
826 theChar = theImage[byteIndex++];
830 theChar = theImage[byteIndex++];
831 blue = theChar & 0x1F;
832 green = (theChar & 0xE0) >> 5;
833 theChar = theImage[byteIndex++];
834 green |= (theChar & 0x07) << 3;
835 red = (theChar & 0xF8) >> 3;
839 if (red+green+blue > threshold)
845 pXorBits[xorIndex] |= bitShifted;
859 bitShifted = bitShifted << 1;
862 rscale = 1 << (16 - rbits);
863 gscale = 1 << (16 - gbits);
864 bscale = 1 << (16 - bbits);
867 fg.red = rfg * rscale / fgBits;
868 fg.green = gfg * gscale / fgBits;
869 fg.blue = bfg * bscale / fgBits;
871 else fg.red = fg.green = fg.blue = 0;
872 bgBits = xmax * ymax - fgBits;
875 bg.red = rbg * rscale / bgBits;
876 bg.green = gbg * gscale / bgBits;
877 bg.blue = bbg * bscale / bgBits;
879 else bg.red = bg.green = bg.blue = 0;
880 pixmapBits = XCreateBitmapFromData( display, root_window, (char *)pXorBits, xmax, ymax );
883 HeapFree( GetProcessHeap(), 0, bitMask32 );
884 XFreePixmap( display, pixmapAll );
885 XFreeGC( display, gc );
887 XDestroyImage( image );
892 XPutImage( display, pixmapAll, gc, image,
893 0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
894 XSetFunction( display, gc, GXcopy );
896 XCopyArea( display, pixmapBits, pixmapAll, gc,
897 0, 0, xmax, ymax, 0, ptr->nHeight );
898 XFreePixmap( display, pixmapBits );
901 XDestroyImage( image );
903 /* Now create the 2 pixmaps for bits and mask */
905 pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
908 pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
909 pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
911 /* Make sure everything went OK so far */
912 if (pixmapBits && pixmapMask && pixmapMaskInv)
914 /* We have to do some magic here, as cursors are not fully
915 * compatible between Windows and X11. Under X11, there are
916 * only 3 possible color cursor: black, white and masked. So
917 * we map the 4th Windows color (invert the bits on the screen)
918 * to black and an additional white bit on an other place
919 * (+1,+1). This require some boolean arithmetic:
922 * And Xor Result | Bits Mask Result
923 * 0 0 black | 0 1 background
924 * 0 1 white | 1 1 foreground
925 * 1 0 no change | X 0 no change
926 * 1 1 inverted | 0 1 background
929 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
930 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
932 * FIXME: apparently some servers do support 'inverted' color.
933 * I don't know if it's correct per the X spec, but maybe we
934 * ought to take advantage of it. -- AJ
936 XSetFunction( display, gc, GXcopy );
937 XCopyArea( display, pixmapAll, pixmapBits, gc,
938 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
939 XCopyArea( display, pixmapAll, pixmapMask, gc,
940 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
941 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
942 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
943 XSetFunction( display, gc, GXand );
944 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
945 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
946 XSetFunction( display, gc, GXandReverse );
947 XCopyArea( display, pixmapAll, pixmapBits, gc,
948 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
949 XSetFunction( display, gc, GXorReverse );
950 XCopyArea( display, pixmapAll, pixmapMask, gc,
951 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
952 /* Additional white */
953 XSetFunction( display, gc, GXor );
954 XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
955 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
956 XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
957 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
958 XSetFunction( display, gc, GXcopy );
963 pixmapMask = XCreateBitmapFromData( display, root_window,
964 bitMask32, ptr->nWidth,
968 /* Make sure hotspot is valid */
969 hotspot.x = ptr->ptHotSpot.x;
970 hotspot.y = ptr->ptHotSpot.y;
971 if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
972 hotspot.y < 0 || hotspot.y >= ptr->nHeight)
974 hotspot.x = ptr->nWidth / 2;
975 hotspot.y = ptr->nHeight / 2;
978 if (pixmapBits && pixmapMask)
979 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
980 &fg, &bg, hotspot.x, hotspot.y );
982 /* Now free everything */
984 if (pixmapAll) XFreePixmap( display, pixmapAll );
985 if (pixmapBits) XFreePixmap( display, pixmapBits );
986 if (pixmapMask) XFreePixmap( display, pixmapMask );
987 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
988 HeapFree( GetProcessHeap(), 0, bitMask32 );
989 XFreeGC( display, gc );
994 /***********************************************************************
995 * CreateCursorIcon (X11DRV.@)
997 void CDECL X11DRV_CreateCursorIcon( HCURSOR handle, CURSORICONINFO *info )
999 static const WORD ICON_HOTSPOT = 0x4242;
1002 /* ignore icons (FIXME: shouldn't use magic hotspot value) */
1003 if (info->ptHotSpot.x == ICON_HOTSPOT && info->ptHotSpot.y == ICON_HOTSPOT) return;
1006 cursor = create_cursor( gdi_display, info );
1009 if (!cursor_context) cursor_context = XUniqueContext();
1010 XSaveContext( gdi_display, (XID)handle, cursor_context, (char *)cursor );
1011 TRACE( "cursor %p %ux%u, planes %u, bpp %u -> xid %lx\n",
1012 handle, info->nWidth, info->nHeight, info->bPlanes, info->bBitsPerPixel, cursor );
1014 wine_tsx11_unlock();
1017 /***********************************************************************
1018 * DestroyCursorIcon (X11DRV.@)
1020 void CDECL X11DRV_DestroyCursorIcon( HCURSOR handle )
1025 if ((cursor = get_x11_cursor( handle )))
1027 TRACE( "%p xid %lx\n", handle, cursor );
1028 XFreeCursor( gdi_display, cursor );
1029 XDeleteContext( gdi_display, (XID)handle, cursor_context );
1031 wine_tsx11_unlock();
1034 /***********************************************************************
1035 * SetCursor (X11DRV.@)
1037 void CDECL X11DRV_SetCursor( HCURSOR handle )
1039 if (cursor_window) SendNotifyMessageW( cursor_window, WM_X11DRV_SET_CURSOR, 0, (LPARAM)handle );
1042 /***********************************************************************
1043 * SetCursorPos (X11DRV.@)
1045 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y )
1047 Display *display = thread_init_display();
1050 TRACE( "warping to (%d,%d)\n", x, y );
1053 if (cursor_pos.x == x && cursor_pos.y == y)
1055 wine_tsx11_unlock();
1056 /* We still need to generate WM_MOUSEMOVE */
1057 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
1062 clip_point_to_rect( &cursor_clip, &pt);
1063 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
1064 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
1065 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
1067 wine_tsx11_unlock();
1071 /***********************************************************************
1072 * GetCursorPos (X11DRV.@)
1074 BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos)
1076 Display *display = thread_init_display();
1078 int rootX, rootY, winX, winY;
1079 unsigned int xstate;
1082 if ((GetTickCount() - last_time_modified > 100) &&
1083 XQueryPointer( display, root_window, &root, &child,
1084 &rootX, &rootY, &winX, &winY, &xstate ))
1086 update_button_state( xstate );
1087 winX += virtual_screen_rect.left;
1088 winY += virtual_screen_rect.top;
1089 TRACE("pointer at (%d,%d)\n", winX, winY );
1090 cursor_pos.x = winX;
1091 cursor_pos.y = winY;
1094 wine_tsx11_unlock();
1099 /***********************************************************************
1100 * ClipCursor (X11DRV.@)
1102 * Set the cursor clipping rectangle.
1104 BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
1106 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
1107 cursor_clip = virtual_screen_rect;
1112 /***********************************************************************
1113 * X11DRV_ButtonPress
1115 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
1117 XButtonEvent *event = &xev->xbutton;
1118 int buttonNum = event->button - 1;
1122 if (buttonNum >= NB_BUTTONS) return;
1128 wData = WHEEL_DELTA;
1131 wData = -WHEEL_DELTA;
1147 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1149 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1150 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1154 /***********************************************************************
1155 * X11DRV_ButtonRelease
1157 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
1159 XButtonEvent *event = &xev->xbutton;
1160 int buttonNum = event->button - 1;
1164 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
1183 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1185 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1186 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1190 /***********************************************************************
1191 * X11DRV_MotionNotify
1193 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
1195 XMotionEvent *event = &xev->xmotion;
1198 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
1202 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1204 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1205 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1209 /***********************************************************************
1210 * X11DRV_EnterNotify
1212 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
1214 XCrossingEvent *event = &xev->xcrossing;
1217 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
1220 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
1221 if (event->window == x11drv_thread_data()->grab_window) return;
1223 /* simulate a mouse motion event */
1224 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1226 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1227 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );