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
)) 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
);
314 req
->wparam
= MAKEWPARAM( get_key_state(), data
);
319 req
->info
= extra_info
;
320 wine_server_call( req
);
321 cursor
= (reply
->count
>= 0) ? wine_server_ptr_handle(reply
->cursor
) : 0;
328 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
329 if (data
&& cursor
!= data
->cursor
)
332 if ((xcursor
= get_x11_cursor( cursor
)))
333 XDefineCursor( gdi_display
, data
->whole_window
, xcursor
);
334 data
->cursor
= cursor
;
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
)
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;
361 if (cursor_pos
.x
== x
&& cursor_pos
.y
== y
&&
362 (flags
& ~(MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
)))
363 flags
&= ~MOUSEEVENTF_MOVE
;
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)
377 if ((abs(x
) > accel
[1]) && (accel
[2] == 2)) xMult
= 4;
379 if (abs(y
) > accel
[0] && accel
[2] != 0)
382 if ((abs(y
) > accel
[1]) && (accel
[2] == 2)) yMult
= 4;
386 pt
.x
= cursor_pos
.x
+ (long)x
* xMult
;
387 pt
.y
= cursor_pos
.y
+ (long)y
* yMult
;
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
);
409 clip_point_to_rect( &cursor_clip
, &pt
);
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 /***********************************************************************
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
)
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)
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,
541 unsigned char *and_bits
, *and_ptr
, *xor_bits
, *xor_ptr
;
542 int and_width_bytes
, xor_width_bytes
;
543 XcursorPixel
*pixel_ptr
;
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
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
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
)
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;
596 /* BGR, 8 bits each */
597 *pixel_ptr
= *xor_ptr
++;
598 *pixel_ptr
|= *xor_ptr
++ << 8;
599 *pixel_ptr
|= *xor_ptr
++ << 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;
613 if (*xor_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
= 0xffffff;
615 if ((x
& 7) == 7) ++xor_ptr
;
619 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr
->bBitsPerPixel
);
626 if (~*and_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
|= 0xff << 24;
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
;
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
)
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;
670 cursor
= pXcursorImageLoadCursor( display
, image
);
671 pXcursorImageDestroy( image
);
676 #endif /* SONAME_LIBXCURSOR */
679 /***********************************************************************
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
;
688 Cursor cursor
= None
;
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
);
699 /* Create the X cursor from the bits */
704 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
705 ptr
->nWidth
, ptr
->nHeight
, ptr
->bPlanes
, ptr
->bBitsPerPixel
,
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
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 )))
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
);
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 );
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
)
755 bitMask32
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
,
756 ptr
->nWidth
* ptr
->nHeight
/ 8 );
771 FIXME("Currently no support for cursors with %d bits per pixel\n",
773 XFreePixmap( display
, pixmapAll
);
774 XFreeGC( display
, gc
);
776 XDestroyImage( image
);
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;
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
)
807 theChar
= theImage
[byteIndex
++];
809 theChar
= theImage
[byteIndex
++];
811 theChar
= theImage
[byteIndex
++];
813 theChar
= theImage
[byteIndex
++];
814 /* If the alpha channel is >5% transparent,
815 * assume that we can add it to the bitMask32.
818 *(bitMask32
+ (y
*xmax
+x
)/8) |= 1 << (x
& 7);
821 theChar
= theImage
[byteIndex
++];
823 theChar
= theImage
[byteIndex
++];
825 theChar
= theImage
[byteIndex
++];
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;
838 if (red
+green
+blue
> threshold
)
844 pXorBits
[xorIndex
] |= bitShifted
;
858 bitShifted
= bitShifted
<< 1;
861 rscale
= 1 << (16 - rbits
);
862 gscale
= 1 << (16 - gbits
);
863 bscale
= 1 << (16 - bbits
);
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
;
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
);
882 HeapFree( GetProcessHeap(), 0, bitMask32
);
883 XFreePixmap( display
, pixmapAll
);
884 XFreeGC( display
, gc
);
886 XDestroyImage( image
);
891 XPutImage( display
, pixmapAll
, gc
, image
,
892 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
);
893 XSetFunction( display
, gc
, GXcopy
);
895 XCopyArea( display
, pixmapBits
, pixmapAll
, gc
,
896 0, 0, xmax
, ymax
, 0, ptr
->nHeight
);
897 XFreePixmap( display
, pixmapBits
);
900 XDestroyImage( image
);
902 /* Now create the 2 pixmaps for bits and mask */
904 pixmapBits
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
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:
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
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
);
962 pixmapMask
= XCreateBitmapFromData( display
, root_window
,
963 bitMask32
, ptr
->nWidth
,
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
);
993 /***********************************************************************
994 * CreateCursorIcon (X11DRV.@)
996 void CDECL
X11DRV_CreateCursorIcon( HCURSOR handle
, CURSORICONINFO
*info
)
998 static const WORD ICON_HOTSPOT
= 0x4242;
1001 /* ignore icons (FIXME: shouldn't use magic hotspot value) */
1002 if (info
->ptHotSpot
.x
== ICON_HOTSPOT
&& info
->ptHotSpot
.y
== ICON_HOTSPOT
) return;
1005 cursor
= create_cursor( gdi_display
, info
);
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
)
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();
1049 TRACE( "warping to (%d,%d)\n", x
, y
);
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 );
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 */
1066 wine_tsx11_unlock();
1070 /***********************************************************************
1071 * GetCursorPos (X11DRV.@)
1073 BOOL CDECL
X11DRV_GetCursorPos(LPPOINT pos
)
1075 Display
*display
= thread_init_display();
1077 int rootX
, rootY
, winX
, winY
;
1078 unsigned int xstate
;
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
;
1093 wine_tsx11_unlock();
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
;
1111 /***********************************************************************
1112 * X11DRV_ButtonPress
1114 void X11DRV_ButtonPress( HWND hwnd
, XEvent
*xev
)
1116 XButtonEvent
*event
= &xev
->xbutton
;
1117 int buttonNum
= event
->button
- 1;
1121 if (buttonNum
>= NB_BUTTONS
) return;
1127 wData
= WHEEL_DELTA
;
1130 wData
= -WHEEL_DELTA
;
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;
1163 if (buttonNum
>= NB_BUTTONS
|| !button_up_flags
[buttonNum
]) return;
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
;
1197 TRACE("hwnd %p, event->is_hint %d\n", hwnd
, event
->is_hint
);
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
;
1216 TRACE("hwnd %p, event->detail %d\n", hwnd
, event
->detail
);
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 );