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 DWORD last_time_modified;
90 static RECT cursor_clip; /* Cursor clipping rect */
92 BOOL X11DRV_SetCursorPos( INT x, INT y );
95 /***********************************************************************
98 * Load the Xcursor library for use.
100 void X11DRV_Xcursor_Init(void)
102 #ifdef SONAME_LIBXCURSOR
103 xcursor_handle = wine_dlopen(SONAME_LIBXCURSOR, RTLD_NOW, NULL, 0);
104 if (!xcursor_handle) /* wine_dlopen failed. */
106 WARN("Xcursor failed to load. Using fallback code.\n");
109 #define LOAD_FUNCPTR(f) \
110 p##f = wine_dlsym(xcursor_handle, #f, NULL, 0)
112 LOAD_FUNCPTR(XcursorImageCreate);
113 LOAD_FUNCPTR(XcursorImageDestroy);
114 LOAD_FUNCPTR(XcursorImageLoadCursor);
116 #endif /* SONAME_LIBXCURSOR */
120 /***********************************************************************
123 * get the coordinates of a mouse event
125 static inline void get_coords( HWND hwnd, Window window, int x, int y, POINT *pt )
127 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
131 if (window == data->client_window)
133 pt->x = x + data->client_rect.left;
134 pt->y = y + data->client_rect.top;
138 pt->x = x + data->whole_rect.left;
139 pt->y = y + data->whole_rect.top;
143 /***********************************************************************
146 * Clip point to the provided rectangle
148 static inline void clip_point_to_rect( LPCRECT rect, LPPOINT pt )
150 if (pt->x < rect->left) pt->x = rect->left;
151 else if (pt->x >= rect->right) pt->x = rect->right - 1;
152 if (pt->y < rect->top) pt->y = rect->top;
153 else if (pt->y >= rect->bottom) pt->y = rect->bottom - 1;
156 /***********************************************************************
157 * update_button_state
159 * Update the button state with what X provides us
161 static inline void update_button_state( unsigned int state )
163 key_state_table[VK_LBUTTON] = (state & Button1Mask ? 0x80 : 0);
164 key_state_table[VK_MBUTTON] = (state & Button2Mask ? 0x80 : 0);
165 key_state_table[VK_RBUTTON] = (state & Button3Mask ? 0x80 : 0);
166 /* X-buttons are not reported from XQueryPointer */
170 /***********************************************************************
173 * Update the various window states on a mouse event.
175 static void update_mouse_state( HWND hwnd, Window window, int x, int y, unsigned int state, POINT *pt )
177 struct x11drv_thread_data *data = x11drv_thread_data();
179 get_coords( hwnd, window, x, y, pt );
181 /* update the cursor */
183 if (data->cursor_window != window)
185 data->cursor_window = window;
187 if (data->cursor) XDefineCursor( data->display, window, data->cursor );
191 /* update the wine server Z-order */
193 if (window != data->grab_window &&
194 /* ignore event if a button is pressed, since the mouse is then grabbed too */
195 !(state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask|Button6Mask|Button7Mask)))
197 SERVER_START_REQ( update_window_zorder )
199 req->window = wine_server_user_handle( hwnd );
200 req->rect.left = pt->x;
201 req->rect.top = pt->y;
202 req->rect.right = pt->x + 1;
203 req->rect.bottom = pt->y + 1;
204 wine_server_call( req );
211 /***********************************************************************
214 static WORD get_key_state(void)
218 if (GetSystemMetrics( SM_SWAPBUTTON ))
220 if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_LBUTTON;
221 if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_RBUTTON;
225 if (key_state_table[VK_LBUTTON] & 0x80) ret |= MK_LBUTTON;
226 if (key_state_table[VK_RBUTTON] & 0x80) ret |= MK_RBUTTON;
228 if (key_state_table[VK_MBUTTON] & 0x80) ret |= MK_MBUTTON;
229 if (key_state_table[VK_SHIFT] & 0x80) ret |= MK_SHIFT;
230 if (key_state_table[VK_CONTROL] & 0x80) ret |= MK_CONTROL;
231 if (key_state_table[VK_XBUTTON1] & 0x80) ret |= MK_XBUTTON1;
232 if (key_state_table[VK_XBUTTON2] & 0x80) ret |= MK_XBUTTON2;
237 /***********************************************************************
238 * queue_raw_mouse_message
240 static void queue_raw_mouse_message( UINT message, HWND hwnd, DWORD x, DWORD y,
241 DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
247 hook.mouseData = MAKELONG( 0, data );
248 hook.flags = injected_flags;
250 hook.dwExtraInfo = extra_info;
252 last_time_modified = GetTickCount();
253 if (HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE )) return;
255 SERVER_START_REQ( send_hardware_message )
257 req->id = (injected_flags & LLMHF_INJECTED) ? 0 : GetCurrentThreadId();
258 req->win = wine_server_user_handle( hwnd );
260 req->wparam = MAKEWPARAM( get_key_state(), data );
265 req->info = extra_info;
266 wine_server_call( req );
273 /***********************************************************************
274 * X11DRV_send_mouse_input
276 void X11DRV_send_mouse_input( HWND hwnd, DWORD flags, DWORD x, DWORD y,
277 DWORD data, DWORD time, DWORD extra_info, UINT injected_flags )
281 if (flags & MOUSEEVENTF_MOVE && flags & MOUSEEVENTF_ABSOLUTE)
283 if (injected_flags & LLMHF_INJECTED)
285 pt.x = (x * screen_width) >> 16;
286 pt.y = (y * screen_height) >> 16;
293 if (cursor_pos.x == x && cursor_pos.y == y &&
294 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
295 flags &= ~MOUSEEVENTF_MOVE;
299 else if (flags & MOUSEEVENTF_MOVE)
301 int accel[3], xMult = 1, yMult = 1;
303 /* dx and dy can be negative numbers for relative movements */
304 SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0);
306 if (abs(x) > accel[0] && accel[2] != 0)
309 if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
311 if (abs(y) > accel[0] && accel[2] != 0)
314 if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
318 pt.x = cursor_pos.x + (long)x * xMult;
319 pt.y = cursor_pos.y + (long)y * yMult;
329 if (flags & MOUSEEVENTF_MOVE)
331 queue_raw_mouse_message( WM_MOUSEMOVE, hwnd, pt.x, pt.y, data, time,
332 extra_info, injected_flags );
333 if ((injected_flags & LLMHF_INJECTED) &&
334 ((flags & MOUSEEVENTF_ABSOLUTE) || x || y)) /* we have to actually move the cursor */
336 X11DRV_SetCursorPos( pt.x, pt.y );
341 clip_point_to_rect( &cursor_clip, &pt);
346 if (flags & MOUSEEVENTF_LEFTDOWN)
348 key_state_table[VK_LBUTTON] |= 0xc0;
349 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
350 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
352 if (flags & MOUSEEVENTF_LEFTUP)
354 key_state_table[VK_LBUTTON] &= ~0x80;
355 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
356 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
358 if (flags & MOUSEEVENTF_RIGHTDOWN)
360 key_state_table[VK_RBUTTON] |= 0xc0;
361 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
362 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
364 if (flags & MOUSEEVENTF_RIGHTUP)
366 key_state_table[VK_RBUTTON] &= ~0x80;
367 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
368 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
370 if (flags & MOUSEEVENTF_MIDDLEDOWN)
372 key_state_table[VK_MBUTTON] |= 0xc0;
373 queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
374 extra_info, injected_flags );
376 if (flags & MOUSEEVENTF_MIDDLEUP)
378 key_state_table[VK_MBUTTON] &= ~0x80;
379 queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
380 extra_info, injected_flags );
382 if (flags & MOUSEEVENTF_WHEEL)
384 queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
385 extra_info, injected_flags );
387 if (flags & MOUSEEVENTF_XDOWN)
389 key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
390 queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
391 extra_info, injected_flags );
393 if (flags & MOUSEEVENTF_XUP)
395 key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
396 queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
397 extra_info, injected_flags );
402 #ifdef SONAME_LIBXCURSOR
404 /***********************************************************************
405 * create_cursor_image
407 * Create an XcursorImage from a CURSORICONINFO
409 static XcursorImage *create_cursor_image( CURSORICONINFO *ptr )
411 static const unsigned char convert_5to8[] =
413 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
414 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
415 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
416 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
418 static const unsigned char convert_6to8[] =
420 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
421 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
422 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
423 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
424 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
425 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
426 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
427 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
432 unsigned char *and_bits, *and_ptr, *xor_bits, *xor_ptr;
433 int and_width_bytes, xor_width_bytes;
434 XcursorPixel *pixel_ptr;
437 BOOL alpha_zero = TRUE;
439 and_width_bytes = ptr->nWidth / 8;
440 xor_width_bytes = and_width_bytes * ptr->bBitsPerPixel;
442 and_size = ptr->nWidth * ptr->nHeight / 8;
443 and_ptr = and_bits = (unsigned char *)(ptr + 1);
445 xor_ptr = xor_bits = and_ptr + and_size;
447 image = pXcursorImageCreate( ptr->nWidth, ptr->nHeight );
448 pixel_ptr = image->pixels;
450 /* Generally 32 bit bitmaps have an alpha channel which is used in favor
451 * of the AND mask. However, if all pixels have alpha = 0x00, the bitmap
452 * is treated like one without alpha and the masks are used. As soon as
453 * one pixel has alpha != 0x00, and the mask ignored as described in the
456 * This is most likely for applications which create the bitmaps with
457 * CreateDIBitmap, which creates a device dependent bitmap, so the format
458 * that arrives when loading depends on the screen's bpp. Apps that were
459 * written at 8 / 16 bpp times do not know about the 32 bit alpha, so
460 * they would get a completely transparent cursor on 32 bit displays.
462 * Non-32 bit bitmaps always use the AND mask
464 if(ptr->bBitsPerPixel == 32)
466 for (y = 0; alpha_zero && y < ptr->nHeight; ++y)
468 xor_ptr = xor_bits + (y * xor_width_bytes);
469 for (x = 0; x < ptr->nWidth; ++x)
471 if (xor_ptr[3] != 0x00)
481 /* On windows, to calculate the color for a pixel, first an AND is done
482 * with the background and the "and" bitmap, then an XOR with the "xor"
483 * bitmap. This means that when the data in the "and" bitmap is 0, the
484 * pixel will get the color as specified in the "xor" bitmap.
485 * However, if the data in the "and" bitmap is 1, the result will be the
486 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
487 * data is completely black (0x000000) the pixel will become transparent,
488 * in case it's white (0xffffff) the pixel will become the inverse of the
491 * Since we can't support inverting colors, we map the grayscale value of
492 * the "xor" data to the alpha channel, and xor the color with either
495 for (y = 0; y < ptr->nHeight; ++y)
497 and_ptr = and_bits + (y * and_width_bytes);
498 xor_ptr = xor_bits + (y * xor_width_bytes);
500 for (x = 0; x < ptr->nWidth; ++x)
502 /* Xcursor pixel data is in ARGB format, with A in the high byte */
503 switch (ptr->bBitsPerPixel)
506 /* BGRA, 8 bits each */
507 *pixel_ptr = *xor_ptr++;
508 *pixel_ptr |= *xor_ptr++ << 8;
509 *pixel_ptr |= *xor_ptr++ << 16;
510 *pixel_ptr |= *xor_ptr++ << 24;
514 /* BGR, 8 bits each */
515 *pixel_ptr = *xor_ptr++;
516 *pixel_ptr |= *xor_ptr++ << 8;
517 *pixel_ptr |= *xor_ptr++ << 16;
521 /* BGR, 5 red, 6 green, 5 blue */
522 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
523 *pixel_ptr = convert_5to8[*xor_ptr & 0x1f];
524 tmp = (*xor_ptr++ & 0xe0) >> 5;
525 tmp |= (*xor_ptr & 0x07) << 3;
526 *pixel_ptr |= convert_6to8[tmp] << 16;
527 *pixel_ptr |= convert_5to8[*xor_ptr & 0xf8] << 24;
531 if (*xor_ptr & (1 << (7 - (x & 7)))) *pixel_ptr = 0xffffff;
533 if ((x & 7) == 7) ++xor_ptr;
537 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr->bBitsPerPixel);
544 if (~*and_ptr & (1 << (7 - (x & 7)))) *pixel_ptr |= 0xff << 24;
547 int alpha = (*pixel_ptr & 0xff) * 0.30f
548 + ((*pixel_ptr & 0xff00) >> 8) * 0.55f
549 + ((*pixel_ptr & 0xff0000) >> 16) * 0.15f;
550 *pixel_ptr ^= ((x + y) % 2) ? 0xffffff : 0x000000;
551 *pixel_ptr |= alpha << 24;
553 if ((x & 7) == 7) ++and_ptr;
563 /***********************************************************************
564 * create_xcursor_cursor
566 * Use Xcursor to create an X cursor from a Windows one.
568 static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
573 if (!ptr) /* Create an empty cursor */
575 image = pXcursorImageCreate( 1, 1 );
578 *(image->pixels) = 0;
579 cursor = pXcursorImageLoadCursor( display, image );
580 pXcursorImageDestroy( image );
585 image = create_cursor_image( ptr );
586 if (!image) return 0;
588 /* Make sure hotspot is valid */
589 image->xhot = ptr->ptHotSpot.x;
590 image->yhot = ptr->ptHotSpot.y;
591 if (image->xhot >= image->width ||
592 image->yhot >= image->height)
594 image->xhot = image->width / 2;
595 image->yhot = image->height / 2;
600 cursor = pXcursorImageLoadCursor( display, image );
601 pXcursorImageDestroy( image );
606 #endif /* SONAME_LIBXCURSOR */
609 /***********************************************************************
612 * Create an X cursor from a Windows one.
614 static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
616 Pixmap pixmapBits, pixmapMask, pixmapMaskInv = 0, pixmapAll;
618 Cursor cursor = None;
620 char *bitMask32 = NULL;
622 #ifdef SONAME_LIBXCURSOR
623 if (pXcursorImageLoadCursor) return create_xcursor_cursor( display, ptr );
626 if (!ptr) /* Create an empty cursor */
628 static const char data[] = { 0 };
630 bg.red = bg.green = bg.blue = 0x0000;
631 pixmapBits = XCreateBitmapFromData( display, root_window, data, 1, 1 );
634 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
636 XFreePixmap( display, pixmapBits );
639 else /* Create the X cursor from the bits */
644 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
645 ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
648 /* Create a pixmap and transfer all the bits to it */
650 /* NOTE: Following hack works, but only because XFree depth
651 * 1 images really use 1 bit/pixel (and so the same layout
652 * as the Windows cursor data). Perhaps use a more generic
655 /* This pixmap will be written with two bitmaps. The first is
656 * the mask and the second is the image.
658 if (!(pixmapAll = XCreatePixmap( display, root_window,
659 ptr->nWidth, ptr->nHeight * 2, 1 )))
661 if (!(image = XCreateImage( display, visual,
662 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
663 ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
665 XFreePixmap( display, pixmapAll );
668 gc = XCreateGC( display, pixmapAll, 0, NULL );
669 XSetGraphicsExposures( display, gc, False );
670 image->byte_order = MSBFirst;
671 image->bitmap_bit_order = MSBFirst;
672 image->bitmap_unit = 16;
673 _XInitImageFuncPtrs(image);
674 if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
676 /* A plain old white on black cursor. */
677 fg.red = fg.green = fg.blue = 0xffff;
678 bg.red = bg.green = bg.blue = 0x0000;
679 XPutImage( display, pixmapAll, gc, image,
680 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
684 int rbits, gbits, bbits, red, green, blue;
685 int rfg, gfg, bfg, rbg, gbg, bbg;
686 int rscale, gscale, bscale;
687 int x, y, xmax, ymax, byteIndex, xorIndex;
688 unsigned char *theMask, *theImage, theChar;
689 int threshold, fgBits, bgBits, bitShifted;
690 BYTE pXorBits[128]; /* Up to 32x32 icons */
692 switch (ptr->bBitsPerPixel)
695 bitMask32 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
696 ptr->nWidth * ptr->nHeight / 8 );
711 FIXME("Currently no support for cursors with %d bits per pixel\n",
713 XFreePixmap( display, pixmapAll );
714 XFreeGC( display, gc );
716 XDestroyImage( image );
719 /* The location of the mask. */
720 theMask = (unsigned char *)(ptr + 1);
721 /* The mask should still be 1 bit per pixel. The color image
722 * should immediately follow the mask.
724 theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
725 rfg = gfg = bfg = rbg = gbg = bbg = 0;
730 xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
731 if (ptr->nWidth > 32) {
732 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
733 ptr->nWidth, ptr->nHeight);
735 ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
737 memset(pXorBits, 0, 128);
738 for (y=0; y<ymax; y++)
740 for (x=0; x<xmax; x++)
742 red = green = blue = 0;
743 switch (ptr->bBitsPerPixel)
746 theChar = theImage[byteIndex++];
748 theChar = theImage[byteIndex++];
750 theChar = theImage[byteIndex++];
752 theChar = theImage[byteIndex++];
753 /* If the alpha channel is >5% transparent,
754 * assume that we can add it to the bitMask32.
757 *(bitMask32 + (y*xmax+x)/8) |= 1 << (x & 7);
760 theChar = theImage[byteIndex++];
762 theChar = theImage[byteIndex++];
764 theChar = theImage[byteIndex++];
768 theChar = theImage[byteIndex++];
769 blue = theChar & 0x1F;
770 green = (theChar & 0xE0) >> 5;
771 theChar = theImage[byteIndex++];
772 green |= (theChar & 0x07) << 3;
773 red = (theChar & 0xF8) >> 3;
777 if (red+green+blue > threshold)
783 pXorBits[xorIndex] |= bitShifted;
797 bitShifted = bitShifted << 1;
800 rscale = 1 << (16 - rbits);
801 gscale = 1 << (16 - gbits);
802 bscale = 1 << (16 - bbits);
805 fg.red = rfg * rscale / fgBits;
806 fg.green = gfg * gscale / fgBits;
807 fg.blue = bfg * bscale / fgBits;
809 else fg.red = fg.green = fg.blue = 0;
810 bgBits = xmax * ymax - fgBits;
813 bg.red = rbg * rscale / bgBits;
814 bg.green = gbg * gscale / bgBits;
815 bg.blue = bbg * bscale / bgBits;
817 else bg.red = bg.green = bg.blue = 0;
818 pixmapBits = XCreateBitmapFromData( display, root_window, (char *)pXorBits, xmax, ymax );
821 HeapFree( GetProcessHeap(), 0, bitMask32 );
822 XFreePixmap( display, pixmapAll );
823 XFreeGC( display, gc );
825 XDestroyImage( image );
830 XPutImage( display, pixmapAll, gc, image,
831 0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
832 XSetFunction( display, gc, GXcopy );
834 XCopyArea( display, pixmapBits, pixmapAll, gc,
835 0, 0, xmax, ymax, 0, ptr->nHeight );
836 XFreePixmap( display, pixmapBits );
839 XDestroyImage( image );
841 /* Now create the 2 pixmaps for bits and mask */
843 pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
844 if (ptr->bBitsPerPixel != 32)
846 pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
847 pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
849 /* Make sure everything went OK so far */
850 if (pixmapBits && pixmapMask && pixmapMaskInv)
852 /* We have to do some magic here, as cursors are not fully
853 * compatible between Windows and X11. Under X11, there are
854 * only 3 possible color cursor: black, white and masked. So
855 * we map the 4th Windows color (invert the bits on the screen)
856 * to black and an additional white bit on an other place
857 * (+1,+1). This require some boolean arithmetic:
860 * And Xor Result | Bits Mask Result
861 * 0 0 black | 0 1 background
862 * 0 1 white | 1 1 foreground
863 * 1 0 no change | X 0 no change
864 * 1 1 inverted | 0 1 background
867 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
868 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
870 * FIXME: apparently some servers do support 'inverted' color.
871 * I don't know if it's correct per the X spec, but maybe we
872 * ought to take advantage of it. -- AJ
874 XSetFunction( display, gc, GXcopy );
875 XCopyArea( display, pixmapAll, pixmapBits, gc,
876 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
877 XCopyArea( display, pixmapAll, pixmapMask, gc,
878 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
879 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
880 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
881 XSetFunction( display, gc, GXand );
882 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
883 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
884 XSetFunction( display, gc, GXandReverse );
885 XCopyArea( display, pixmapAll, pixmapBits, gc,
886 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
887 XSetFunction( display, gc, GXorReverse );
888 XCopyArea( display, pixmapAll, pixmapMask, gc,
889 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
890 /* Additional white */
891 XSetFunction( display, gc, GXor );
892 XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
893 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
894 XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
895 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
896 XSetFunction( display, gc, GXcopy );
901 pixmapMask = XCreateBitmapFromData( display, root_window,
902 bitMask32, ptr->nWidth,
904 HeapFree( GetProcessHeap(), 0, bitMask32 );
907 /* Make sure hotspot is valid */
908 hotspot.x = ptr->ptHotSpot.x;
909 hotspot.y = ptr->ptHotSpot.y;
910 if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
911 hotspot.y < 0 || hotspot.y >= ptr->nHeight)
913 hotspot.x = ptr->nWidth / 2;
914 hotspot.y = ptr->nHeight / 2;
917 if (pixmapBits && pixmapMask)
918 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
919 &fg, &bg, hotspot.x, hotspot.y );
921 /* Now free everything */
923 if (pixmapAll) XFreePixmap( display, pixmapAll );
924 if (pixmapBits) XFreePixmap( display, pixmapBits );
925 if (pixmapMask) XFreePixmap( display, pixmapMask );
926 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
927 XFreeGC( display, gc );
933 /***********************************************************************
934 * SetCursor (X11DRV.@)
936 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
938 struct x11drv_thread_data *data = x11drv_init_thread_data();
942 TRACE("%ux%u, planes %u, bpp %u\n",
943 lpCursor->nWidth, lpCursor->nHeight, lpCursor->bPlanes, lpCursor->bBitsPerPixel);
947 /* set the same cursor for all top-level windows of the current thread */
950 cursor = create_cursor( data->display, lpCursor );
953 if (data->cursor) XFreeCursor( data->display, data->cursor );
954 data->cursor = cursor;
955 if (data->cursor_window)
957 XDefineCursor( data->display, data->cursor_window, cursor );
958 /* Make the change take effect immediately */
959 XFlush( data->display );
965 /***********************************************************************
966 * SetCursorPos (X11DRV.@)
968 BOOL X11DRV_SetCursorPos( INT x, INT y )
970 Display *display = thread_init_display();
973 TRACE( "warping to (%d,%d)\n", x, y );
976 if (cursor_pos.x == x && cursor_pos.y == y)
979 /* We still need to generate WM_MOUSEMOVE */
980 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
985 clip_point_to_rect( &cursor_clip, &pt);
986 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
987 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
988 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
994 /***********************************************************************
995 * GetCursorPos (X11DRV.@)
997 BOOL X11DRV_GetCursorPos(LPPOINT pos)
999 Display *display = thread_init_display();
1001 int rootX, rootY, winX, winY;
1002 unsigned int xstate;
1005 if ((GetTickCount() - last_time_modified > 100) &&
1006 XQueryPointer( display, root_window, &root, &child,
1007 &rootX, &rootY, &winX, &winY, &xstate ))
1009 update_button_state( xstate );
1010 winX += virtual_screen_rect.left;
1011 winY += virtual_screen_rect.top;
1012 TRACE("pointer at (%d,%d)\n", winX, winY );
1013 cursor_pos.x = winX;
1014 cursor_pos.y = winY;
1017 wine_tsx11_unlock();
1022 /***********************************************************************
1023 * ClipCursor (X11DRV.@)
1025 * Set the cursor clipping rectangle.
1027 BOOL X11DRV_ClipCursor( LPCRECT clip )
1029 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
1030 cursor_clip = virtual_screen_rect;
1035 /***********************************************************************
1036 * X11DRV_ButtonPress
1038 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
1040 XButtonEvent *event = &xev->xbutton;
1041 int buttonNum = event->button - 1;
1045 if (buttonNum >= NB_BUTTONS) return;
1051 wData = WHEEL_DELTA;
1054 wData = -WHEEL_DELTA;
1070 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1072 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1073 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1077 /***********************************************************************
1078 * X11DRV_ButtonRelease
1080 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
1082 XButtonEvent *event = &xev->xbutton;
1083 int buttonNum = event->button - 1;
1087 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
1106 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1108 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1109 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1113 /***********************************************************************
1114 * X11DRV_MotionNotify
1116 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
1118 XMotionEvent *event = &xev->xmotion;
1121 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
1125 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1127 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1128 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1132 /***********************************************************************
1133 * X11DRV_EnterNotify
1135 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
1137 XCrossingEvent *event = &xev->xcrossing;
1140 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
1143 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
1144 if (event->window == x11drv_thread_data()->grab_window) return;
1146 /* simulate a mouse motion event */
1147 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1149 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1150 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );