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 CDECL
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 /***********************************************************************
405 * Generally 32 bit bitmaps have an alpha channel which is used in favor of the
406 * AND mask. However, if all pixels have alpha = 0x00, the bitmap is treated
407 * like one without alpha and the masks are used. As soon as one pixel has
408 * alpha != 0x00, and the mask ignored as described in the docs.
410 * This is most likely for applications which create the bitmaps with
411 * CreateDIBitmap, which creates a device dependent bitmap, so the format that
412 * arrives when loading depends on the screen's bpp. Apps that were written at
413 * 8 / 16 bpp times do not know about the 32 bit alpha, so they would get a
414 * completely transparent cursor on 32 bit displays.
416 * Non-32 bit bitmaps always use the AND mask.
418 static BOOL
check_alpha_zero(CURSORICONINFO
*ptr
, unsigned char *xor_bits
)
421 unsigned char *xor_ptr
;
423 if (ptr
->bBitsPerPixel
== 32)
425 for (y
= 0; y
< ptr
->nHeight
; ++y
)
427 xor_ptr
= xor_bits
+ (y
* ptr
->nWidthBytes
);
428 for (x
= 0; x
< ptr
->nWidth
; ++x
)
430 if (xor_ptr
[3] != 0x00)
443 #ifdef SONAME_LIBXCURSOR
445 /***********************************************************************
446 * create_cursor_image
448 * Create an XcursorImage from a CURSORICONINFO
450 static XcursorImage
*create_cursor_image( CURSORICONINFO
*ptr
)
452 static const unsigned char convert_5to8
[] =
454 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
455 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
456 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
457 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
459 static const unsigned char convert_6to8
[] =
461 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
462 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
463 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
464 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
465 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
466 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
467 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
468 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
473 unsigned char *and_bits
, *and_ptr
, *xor_bits
, *xor_ptr
;
474 int and_width_bytes
, xor_width_bytes
;
475 XcursorPixel
*pixel_ptr
;
480 and_width_bytes
= 2 * ((ptr
->nWidth
+15) / 16);
481 xor_width_bytes
= ptr
->nWidthBytes
;
483 and_size
= ptr
->nHeight
* and_width_bytes
;
484 and_ptr
= and_bits
= (unsigned char *)(ptr
+ 1);
486 xor_ptr
= xor_bits
= and_ptr
+ and_size
;
488 image
= pXcursorImageCreate( ptr
->nWidth
, ptr
->nHeight
);
489 pixel_ptr
= image
->pixels
;
491 alpha_zero
= check_alpha_zero(ptr
, xor_bits
);
493 /* On windows, to calculate the color for a pixel, first an AND is done
494 * with the background and the "and" bitmap, then an XOR with the "xor"
495 * bitmap. This means that when the data in the "and" bitmap is 0, the
496 * pixel will get the color as specified in the "xor" bitmap.
497 * However, if the data in the "and" bitmap is 1, the result will be the
498 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
499 * data is completely black (0x000000) the pixel will become transparent,
500 * in case it's white (0xffffff) the pixel will become the inverse of the
503 * Since we can't support inverting colors, we map the grayscale value of
504 * the "xor" data to the alpha channel, and xor the color with either
507 for (y
= 0; y
< ptr
->nHeight
; ++y
)
509 and_ptr
= and_bits
+ (y
* and_width_bytes
);
510 xor_ptr
= xor_bits
+ (y
* xor_width_bytes
);
512 for (x
= 0; x
< ptr
->nWidth
; ++x
)
514 /* Xcursor pixel data is in ARGB format, with A in the high byte */
515 switch (ptr
->bBitsPerPixel
)
518 /* BGRA, 8 bits each */
519 *pixel_ptr
= *xor_ptr
++;
520 *pixel_ptr
|= *xor_ptr
++ << 8;
521 *pixel_ptr
|= *xor_ptr
++ << 16;
522 *pixel_ptr
|= *xor_ptr
++ << 24;
526 /* BGR, 8 bits each */
527 *pixel_ptr
= *xor_ptr
++;
528 *pixel_ptr
|= *xor_ptr
++ << 8;
529 *pixel_ptr
|= *xor_ptr
++ << 16;
533 /* BGR, 5 red, 6 green, 5 blue */
534 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
535 *pixel_ptr
= convert_5to8
[*xor_ptr
& 0x1f];
536 tmp
= (*xor_ptr
++ & 0xe0) >> 5;
537 tmp
|= (*xor_ptr
& 0x07) << 3;
538 *pixel_ptr
|= convert_6to8
[tmp
] << 16;
539 *pixel_ptr
|= convert_5to8
[*xor_ptr
& 0xf8] << 24;
543 if (*xor_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
= 0xffffff;
545 if ((x
& 7) == 7) ++xor_ptr
;
549 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr
->bBitsPerPixel
);
556 if (~*and_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
|= 0xff << 24;
559 int alpha
= (*pixel_ptr
& 0xff) * 0.30f
560 + ((*pixel_ptr
& 0xff00) >> 8) * 0.55f
561 + ((*pixel_ptr
& 0xff0000) >> 16) * 0.15f
;
562 *pixel_ptr
^= ((x
+ y
) % 2) ? 0xffffff : 0x000000;
563 *pixel_ptr
|= alpha
<< 24;
565 if ((x
& 7) == 7) ++and_ptr
;
575 /***********************************************************************
576 * create_xcursor_cursor
578 * Use Xcursor to create an X cursor from a Windows one.
580 static Cursor
create_xcursor_cursor( Display
*display
, CURSORICONINFO
*ptr
)
585 if (!ptr
) /* Create an empty cursor */
587 image
= pXcursorImageCreate( 1, 1 );
590 *(image
->pixels
) = 0;
591 cursor
= pXcursorImageLoadCursor( display
, image
);
592 pXcursorImageDestroy( image
);
597 image
= create_cursor_image( ptr
);
598 if (!image
) return 0;
600 /* Make sure hotspot is valid */
601 image
->xhot
= ptr
->ptHotSpot
.x
;
602 image
->yhot
= ptr
->ptHotSpot
.y
;
603 if (image
->xhot
>= image
->width
||
604 image
->yhot
>= image
->height
)
606 image
->xhot
= image
->width
/ 2;
607 image
->yhot
= image
->height
/ 2;
612 cursor
= pXcursorImageLoadCursor( display
, image
);
613 pXcursorImageDestroy( image
);
618 #endif /* SONAME_LIBXCURSOR */
621 /***********************************************************************
624 * Create an X cursor from a Windows one.
626 static Cursor
create_cursor( Display
*display
, CURSORICONINFO
*ptr
)
628 Pixmap pixmapBits
, pixmapMask
, pixmapMaskInv
= 0, pixmapAll
;
630 Cursor cursor
= None
;
632 char *bitMask32
= NULL
;
633 BOOL alpha_zero
= TRUE
;
635 #ifdef SONAME_LIBXCURSOR
636 if (pXcursorImageLoadCursor
) return create_xcursor_cursor( display
, ptr
);
639 if (!ptr
) /* Create an empty cursor */
641 static const char data
[] = { 0 };
643 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
644 pixmapBits
= XCreateBitmapFromData( display
, root_window
, data
, 1, 1 );
647 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapBits
,
649 XFreePixmap( display
, pixmapBits
);
652 else /* Create the X cursor from the bits */
657 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
658 ptr
->nWidth
, ptr
->nHeight
, ptr
->bPlanes
, ptr
->bBitsPerPixel
,
661 /* Create a pixmap and transfer all the bits to it */
663 /* NOTE: Following hack works, but only because XFree depth
664 * 1 images really use 1 bit/pixel (and so the same layout
665 * as the Windows cursor data). Perhaps use a more generic
668 /* This pixmap will be written with two bitmaps. The first is
669 * the mask and the second is the image.
671 if (!(pixmapAll
= XCreatePixmap( display
, root_window
,
672 ptr
->nWidth
, ptr
->nHeight
* 2, 1 )))
674 if (!(image
= XCreateImage( display
, visual
,
675 1, ZPixmap
, 0, (char *)(ptr
+ 1), ptr
->nWidth
,
676 ptr
->nHeight
* 2, 16, ptr
->nWidthBytes
/ptr
->bBitsPerPixel
)))
678 XFreePixmap( display
, pixmapAll
);
681 gc
= XCreateGC( display
, pixmapAll
, 0, NULL
);
682 XSetGraphicsExposures( display
, gc
, False
);
683 image
->byte_order
= MSBFirst
;
684 image
->bitmap_bit_order
= MSBFirst
;
685 image
->bitmap_unit
= 16;
686 _XInitImageFuncPtrs(image
);
687 if (ptr
->bPlanes
* ptr
->bBitsPerPixel
== 1)
689 /* A plain old white on black cursor. */
690 fg
.red
= fg
.green
= fg
.blue
= 0xffff;
691 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
692 XPutImage( display
, pixmapAll
, gc
, image
,
693 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
* 2 );
697 int rbits
, gbits
, bbits
, red
, green
, blue
;
698 int rfg
, gfg
, bfg
, rbg
, gbg
, bbg
;
699 int rscale
, gscale
, bscale
;
700 int x
, y
, xmax
, ymax
, byteIndex
, xorIndex
;
701 unsigned char *theMask
, *theImage
, theChar
;
702 int threshold
, fgBits
, bgBits
, bitShifted
;
703 BYTE pXorBits
[128]; /* Up to 32x32 icons */
705 switch (ptr
->bBitsPerPixel
)
708 bitMask32
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
,
709 ptr
->nWidth
* ptr
->nHeight
/ 8 );
724 FIXME("Currently no support for cursors with %d bits per pixel\n",
726 XFreePixmap( display
, pixmapAll
);
727 XFreeGC( display
, gc
);
729 XDestroyImage( image
);
732 /* The location of the mask. */
733 theMask
= (unsigned char *)(ptr
+ 1);
734 /* The mask should still be 1 bit per pixel. The color image
735 * should immediately follow the mask.
737 theImage
= &theMask
[ptr
->nWidth
/8 * ptr
->nHeight
];
738 rfg
= gfg
= bfg
= rbg
= gbg
= bbg
= 0;
743 xmax
= (ptr
->nWidth
> 32) ? 32 : ptr
->nWidth
;
744 if (ptr
->nWidth
> 32) {
745 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
746 ptr
->nWidth
, ptr
->nHeight
);
748 ymax
= (ptr
->nHeight
> 32) ? 32 : ptr
->nHeight
;
749 alpha_zero
= check_alpha_zero(ptr
, theImage
);
751 memset(pXorBits
, 0, 128);
752 for (y
=0; y
<ymax
; y
++)
754 for (x
=0; x
<xmax
; x
++)
756 red
= green
= blue
= 0;
757 switch (ptr
->bBitsPerPixel
)
760 theChar
= theImage
[byteIndex
++];
762 theChar
= theImage
[byteIndex
++];
764 theChar
= theImage
[byteIndex
++];
766 theChar
= theImage
[byteIndex
++];
767 /* If the alpha channel is >5% transparent,
768 * assume that we can add it to the bitMask32.
771 *(bitMask32
+ (y
*xmax
+x
)/8) |= 1 << (x
& 7);
774 theChar
= theImage
[byteIndex
++];
776 theChar
= theImage
[byteIndex
++];
778 theChar
= theImage
[byteIndex
++];
782 theChar
= theImage
[byteIndex
++];
783 blue
= theChar
& 0x1F;
784 green
= (theChar
& 0xE0) >> 5;
785 theChar
= theImage
[byteIndex
++];
786 green
|= (theChar
& 0x07) << 3;
787 red
= (theChar
& 0xF8) >> 3;
791 if (red
+green
+blue
> threshold
)
797 pXorBits
[xorIndex
] |= bitShifted
;
811 bitShifted
= bitShifted
<< 1;
814 rscale
= 1 << (16 - rbits
);
815 gscale
= 1 << (16 - gbits
);
816 bscale
= 1 << (16 - bbits
);
819 fg
.red
= rfg
* rscale
/ fgBits
;
820 fg
.green
= gfg
* gscale
/ fgBits
;
821 fg
.blue
= bfg
* bscale
/ fgBits
;
823 else fg
.red
= fg
.green
= fg
.blue
= 0;
824 bgBits
= xmax
* ymax
- fgBits
;
827 bg
.red
= rbg
* rscale
/ bgBits
;
828 bg
.green
= gbg
* gscale
/ bgBits
;
829 bg
.blue
= bbg
* bscale
/ bgBits
;
831 else bg
.red
= bg
.green
= bg
.blue
= 0;
832 pixmapBits
= XCreateBitmapFromData( display
, root_window
, (char *)pXorBits
, xmax
, ymax
);
835 HeapFree( GetProcessHeap(), 0, bitMask32
);
836 XFreePixmap( display
, pixmapAll
);
837 XFreeGC( display
, gc
);
839 XDestroyImage( image
);
844 XPutImage( display
, pixmapAll
, gc
, image
,
845 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
);
846 XSetFunction( display
, gc
, GXcopy
);
848 XCopyArea( display
, pixmapBits
, pixmapAll
, gc
,
849 0, 0, xmax
, ymax
, 0, ptr
->nHeight
);
850 XFreePixmap( display
, pixmapBits
);
853 XDestroyImage( image
);
855 /* Now create the 2 pixmaps for bits and mask */
857 pixmapBits
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
860 pixmapMaskInv
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
861 pixmapMask
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
863 /* Make sure everything went OK so far */
864 if (pixmapBits
&& pixmapMask
&& pixmapMaskInv
)
866 /* We have to do some magic here, as cursors are not fully
867 * compatible between Windows and X11. Under X11, there are
868 * only 3 possible color cursor: black, white and masked. So
869 * we map the 4th Windows color (invert the bits on the screen)
870 * to black and an additional white bit on an other place
871 * (+1,+1). This require some boolean arithmetic:
874 * And Xor Result | Bits Mask Result
875 * 0 0 black | 0 1 background
876 * 0 1 white | 1 1 foreground
877 * 1 0 no change | X 0 no change
878 * 1 1 inverted | 0 1 background
881 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
882 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
884 * FIXME: apparently some servers do support 'inverted' color.
885 * I don't know if it's correct per the X spec, but maybe we
886 * ought to take advantage of it. -- AJ
888 XSetFunction( display
, gc
, GXcopy
);
889 XCopyArea( display
, pixmapAll
, pixmapBits
, gc
,
890 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
891 XCopyArea( display
, pixmapAll
, pixmapMask
, gc
,
892 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
893 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, gc
,
894 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
895 XSetFunction( display
, gc
, GXand
);
896 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, gc
,
897 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
898 XSetFunction( display
, gc
, GXandReverse
);
899 XCopyArea( display
, pixmapAll
, pixmapBits
, gc
,
900 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
901 XSetFunction( display
, gc
, GXorReverse
);
902 XCopyArea( display
, pixmapAll
, pixmapMask
, gc
,
903 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
904 /* Additional white */
905 XSetFunction( display
, gc
, GXor
);
906 XCopyArea( display
, pixmapMaskInv
, pixmapMask
, gc
,
907 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
908 XCopyArea( display
, pixmapMaskInv
, pixmapBits
, gc
,
909 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
910 XSetFunction( display
, gc
, GXcopy
);
915 pixmapMask
= XCreateBitmapFromData( display
, root_window
,
916 bitMask32
, ptr
->nWidth
,
920 /* Make sure hotspot is valid */
921 hotspot
.x
= ptr
->ptHotSpot
.x
;
922 hotspot
.y
= ptr
->ptHotSpot
.y
;
923 if (hotspot
.x
< 0 || hotspot
.x
>= ptr
->nWidth
||
924 hotspot
.y
< 0 || hotspot
.y
>= ptr
->nHeight
)
926 hotspot
.x
= ptr
->nWidth
/ 2;
927 hotspot
.y
= ptr
->nHeight
/ 2;
930 if (pixmapBits
&& pixmapMask
)
931 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapMask
,
932 &fg
, &bg
, hotspot
.x
, hotspot
.y
);
934 /* Now free everything */
936 if (pixmapAll
) XFreePixmap( display
, pixmapAll
);
937 if (pixmapBits
) XFreePixmap( display
, pixmapBits
);
938 if (pixmapMask
) XFreePixmap( display
, pixmapMask
);
939 if (pixmapMaskInv
) XFreePixmap( display
, pixmapMaskInv
);
940 HeapFree( GetProcessHeap(), 0, bitMask32
);
941 XFreeGC( display
, gc
);
947 /***********************************************************************
948 * SetCursor (X11DRV.@)
950 void CDECL
X11DRV_SetCursor( CURSORICONINFO
*lpCursor
)
952 struct x11drv_thread_data
*data
= x11drv_init_thread_data();
956 TRACE("%ux%u, planes %u, bpp %u\n",
957 lpCursor
->nWidth
, lpCursor
->nHeight
, lpCursor
->bPlanes
, lpCursor
->bBitsPerPixel
);
961 /* set the same cursor for all top-level windows of the current thread */
964 cursor
= create_cursor( data
->display
, lpCursor
);
967 if (data
->cursor
) XFreeCursor( data
->display
, data
->cursor
);
968 data
->cursor
= cursor
;
969 if (data
->cursor_window
)
971 XDefineCursor( data
->display
, data
->cursor_window
, cursor
);
972 /* Make the change take effect immediately */
973 XFlush( data
->display
);
979 /***********************************************************************
980 * SetCursorPos (X11DRV.@)
982 BOOL CDECL
X11DRV_SetCursorPos( INT x
, INT y
)
984 Display
*display
= thread_init_display();
987 TRACE( "warping to (%d,%d)\n", x
, y
);
990 if (cursor_pos
.x
== x
&& cursor_pos
.y
== y
)
993 /* We still need to generate WM_MOUSEMOVE */
994 queue_raw_mouse_message( WM_MOUSEMOVE
, NULL
, x
, y
, 0, GetCurrentTime(), 0, 0 );
999 clip_point_to_rect( &cursor_clip
, &pt
);
1000 XWarpPointer( display
, root_window
, root_window
, 0, 0, 0, 0,
1001 pt
.x
- virtual_screen_rect
.left
, pt
.y
- virtual_screen_rect
.top
);
1002 XFlush( display
); /* avoids bad mouse lag in games that do their own mouse warping */
1004 wine_tsx11_unlock();
1008 /***********************************************************************
1009 * GetCursorPos (X11DRV.@)
1011 BOOL CDECL
X11DRV_GetCursorPos(LPPOINT pos
)
1013 Display
*display
= thread_init_display();
1015 int rootX
, rootY
, winX
, winY
;
1016 unsigned int xstate
;
1019 if ((GetTickCount() - last_time_modified
> 100) &&
1020 XQueryPointer( display
, root_window
, &root
, &child
,
1021 &rootX
, &rootY
, &winX
, &winY
, &xstate
))
1023 update_button_state( xstate
);
1024 winX
+= virtual_screen_rect
.left
;
1025 winY
+= virtual_screen_rect
.top
;
1026 TRACE("pointer at (%d,%d)\n", winX
, winY
);
1027 cursor_pos
.x
= winX
;
1028 cursor_pos
.y
= winY
;
1031 wine_tsx11_unlock();
1036 /***********************************************************************
1037 * ClipCursor (X11DRV.@)
1039 * Set the cursor clipping rectangle.
1041 BOOL CDECL
X11DRV_ClipCursor( LPCRECT clip
)
1043 if (!IntersectRect( &cursor_clip
, &virtual_screen_rect
, clip
))
1044 cursor_clip
= virtual_screen_rect
;
1049 /***********************************************************************
1050 * X11DRV_ButtonPress
1052 void X11DRV_ButtonPress( HWND hwnd
, XEvent
*xev
)
1054 XButtonEvent
*event
= &xev
->xbutton
;
1055 int buttonNum
= event
->button
- 1;
1059 if (buttonNum
>= NB_BUTTONS
) return;
1065 wData
= WHEEL_DELTA
;
1068 wData
= -WHEEL_DELTA
;
1084 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1086 X11DRV_send_mouse_input( hwnd
, button_down_flags
[buttonNum
] | MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_MOVE
,
1087 pt
.x
, pt
.y
, wData
, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1091 /***********************************************************************
1092 * X11DRV_ButtonRelease
1094 void X11DRV_ButtonRelease( HWND hwnd
, XEvent
*xev
)
1096 XButtonEvent
*event
= &xev
->xbutton
;
1097 int buttonNum
= event
->button
- 1;
1101 if (buttonNum
>= NB_BUTTONS
|| !button_up_flags
[buttonNum
]) return;
1120 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1122 X11DRV_send_mouse_input( hwnd
, button_up_flags
[buttonNum
] | MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_MOVE
,
1123 pt
.x
, pt
.y
, wData
, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1127 /***********************************************************************
1128 * X11DRV_MotionNotify
1130 void X11DRV_MotionNotify( HWND hwnd
, XEvent
*xev
)
1132 XMotionEvent
*event
= &xev
->xmotion
;
1135 TRACE("hwnd %p, event->is_hint %d\n", hwnd
, event
->is_hint
);
1139 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1141 X11DRV_send_mouse_input( hwnd
, MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
1142 pt
.x
, pt
.y
, 0, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1146 /***********************************************************************
1147 * X11DRV_EnterNotify
1149 void X11DRV_EnterNotify( HWND hwnd
, XEvent
*xev
)
1151 XCrossingEvent
*event
= &xev
->xcrossing
;
1154 TRACE("hwnd %p, event->detail %d\n", hwnd
, event
->detail
);
1157 if (event
->detail
== NotifyVirtual
|| event
->detail
== NotifyNonlinearVirtual
) return;
1158 if (event
->window
== x11drv_thread_data()->grab_window
) return;
1160 /* simulate a mouse motion event */
1161 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1163 X11DRV_send_mouse_input( hwnd
, MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
1164 pt
.x
, pt
.y
, 0, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );