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 if (!image
) return NULL
;
491 pixel_ptr
= image
->pixels
;
493 alpha_zero
= check_alpha_zero(ptr
, xor_bits
);
495 /* On windows, to calculate the color for a pixel, first an AND is done
496 * with the background and the "and" bitmap, then an XOR with the "xor"
497 * bitmap. This means that when the data in the "and" bitmap is 0, the
498 * pixel will get the color as specified in the "xor" bitmap.
499 * However, if the data in the "and" bitmap is 1, the result will be the
500 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
501 * data is completely black (0x000000) the pixel will become transparent,
502 * in case it's white (0xffffff) the pixel will become the inverse of the
505 * Since we can't support inverting colors, we map the grayscale value of
506 * the "xor" data to the alpha channel, and xor the color with either
509 for (y
= 0; y
< ptr
->nHeight
; ++y
)
511 and_ptr
= and_bits
+ (y
* and_width_bytes
);
512 xor_ptr
= xor_bits
+ (y
* xor_width_bytes
);
514 for (x
= 0; x
< ptr
->nWidth
; ++x
)
516 /* Xcursor pixel data is in ARGB format, with A in the high byte */
517 switch (ptr
->bBitsPerPixel
)
520 /* BGRA, 8 bits each */
521 *pixel_ptr
= *xor_ptr
++;
522 *pixel_ptr
|= *xor_ptr
++ << 8;
523 *pixel_ptr
|= *xor_ptr
++ << 16;
524 *pixel_ptr
|= *xor_ptr
++ << 24;
528 /* BGR, 8 bits each */
529 *pixel_ptr
= *xor_ptr
++;
530 *pixel_ptr
|= *xor_ptr
++ << 8;
531 *pixel_ptr
|= *xor_ptr
++ << 16;
535 /* BGR, 5 red, 6 green, 5 blue */
536 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
537 *pixel_ptr
= convert_5to8
[*xor_ptr
& 0x1f];
538 tmp
= (*xor_ptr
++ & 0xe0) >> 5;
539 tmp
|= (*xor_ptr
& 0x07) << 3;
540 *pixel_ptr
|= convert_6to8
[tmp
] << 16;
541 *pixel_ptr
|= convert_5to8
[*xor_ptr
& 0xf8] << 24;
545 if (*xor_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
= 0xffffff;
547 if ((x
& 7) == 7) ++xor_ptr
;
551 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr
->bBitsPerPixel
);
558 if (~*and_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
|= 0xff << 24;
561 int alpha
= (*pixel_ptr
& 0xff) * 0.30f
562 + ((*pixel_ptr
& 0xff00) >> 8) * 0.55f
563 + ((*pixel_ptr
& 0xff0000) >> 16) * 0.15f
;
564 *pixel_ptr
^= ((x
+ y
) % 2) ? 0xffffff : 0x000000;
565 *pixel_ptr
|= alpha
<< 24;
567 if ((x
& 7) == 7) ++and_ptr
;
577 /***********************************************************************
578 * create_xcursor_cursor
580 * Use Xcursor to create an X cursor from a Windows one.
582 static Cursor
create_xcursor_cursor( Display
*display
, CURSORICONINFO
*ptr
)
587 if (!ptr
) /* Create an empty cursor */
589 image
= pXcursorImageCreate( 1, 1 );
592 *(image
->pixels
) = 0;
593 cursor
= pXcursorImageLoadCursor( display
, image
);
594 pXcursorImageDestroy( image
);
599 image
= create_cursor_image( ptr
);
600 if (!image
) return 0;
602 /* Make sure hotspot is valid */
603 image
->xhot
= ptr
->ptHotSpot
.x
;
604 image
->yhot
= ptr
->ptHotSpot
.y
;
605 if (image
->xhot
>= image
->width
||
606 image
->yhot
>= image
->height
)
608 image
->xhot
= image
->width
/ 2;
609 image
->yhot
= image
->height
/ 2;
614 cursor
= pXcursorImageLoadCursor( display
, image
);
615 pXcursorImageDestroy( image
);
620 #endif /* SONAME_LIBXCURSOR */
623 /***********************************************************************
626 * Create an X cursor from a Windows one.
628 static Cursor
create_cursor( Display
*display
, CURSORICONINFO
*ptr
)
630 Pixmap pixmapBits
, pixmapMask
, pixmapMaskInv
= 0, pixmapAll
;
632 Cursor cursor
= None
;
634 char *bitMask32
= NULL
;
635 BOOL alpha_zero
= TRUE
;
637 #ifdef SONAME_LIBXCURSOR
638 if (pXcursorImageLoadCursor
) return create_xcursor_cursor( display
, ptr
);
641 if (!ptr
) /* Create an empty cursor */
643 static const char data
[] = { 0 };
645 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
646 pixmapBits
= XCreateBitmapFromData( display
, root_window
, data
, 1, 1 );
649 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapBits
,
651 XFreePixmap( display
, pixmapBits
);
654 else /* Create the X cursor from the bits */
659 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
660 ptr
->nWidth
, ptr
->nHeight
, ptr
->bPlanes
, ptr
->bBitsPerPixel
,
663 /* Create a pixmap and transfer all the bits to it */
665 /* NOTE: Following hack works, but only because XFree depth
666 * 1 images really use 1 bit/pixel (and so the same layout
667 * as the Windows cursor data). Perhaps use a more generic
670 /* This pixmap will be written with two bitmaps. The first is
671 * the mask and the second is the image.
673 if (!(pixmapAll
= XCreatePixmap( display
, root_window
,
674 ptr
->nWidth
, ptr
->nHeight
* 2, 1 )))
676 if (!(image
= XCreateImage( display
, visual
,
677 1, ZPixmap
, 0, (char *)(ptr
+ 1), ptr
->nWidth
,
678 ptr
->nHeight
* 2, 16, ptr
->nWidthBytes
/ptr
->bBitsPerPixel
)))
680 XFreePixmap( display
, pixmapAll
);
683 gc
= XCreateGC( display
, pixmapAll
, 0, NULL
);
684 XSetGraphicsExposures( display
, gc
, False
);
685 image
->byte_order
= MSBFirst
;
686 image
->bitmap_bit_order
= MSBFirst
;
687 image
->bitmap_unit
= 16;
688 _XInitImageFuncPtrs(image
);
689 if (ptr
->bPlanes
* ptr
->bBitsPerPixel
== 1)
691 /* A plain old white on black cursor. */
692 fg
.red
= fg
.green
= fg
.blue
= 0xffff;
693 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
694 XPutImage( display
, pixmapAll
, gc
, image
,
695 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
* 2 );
699 int rbits
, gbits
, bbits
, red
, green
, blue
;
700 int rfg
, gfg
, bfg
, rbg
, gbg
, bbg
;
701 int rscale
, gscale
, bscale
;
702 int x
, y
, xmax
, ymax
, byteIndex
, xorIndex
;
703 unsigned char *theMask
, *theImage
, theChar
;
704 int threshold
, fgBits
, bgBits
, bitShifted
;
705 BYTE pXorBits
[128]; /* Up to 32x32 icons */
707 switch (ptr
->bBitsPerPixel
)
710 bitMask32
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
,
711 ptr
->nWidth
* ptr
->nHeight
/ 8 );
726 FIXME("Currently no support for cursors with %d bits per pixel\n",
728 XFreePixmap( display
, pixmapAll
);
729 XFreeGC( display
, gc
);
731 XDestroyImage( image
);
734 /* The location of the mask. */
735 theMask
= (unsigned char *)(ptr
+ 1);
736 /* The mask should still be 1 bit per pixel. The color image
737 * should immediately follow the mask.
739 theImage
= &theMask
[ptr
->nWidth
/8 * ptr
->nHeight
];
740 rfg
= gfg
= bfg
= rbg
= gbg
= bbg
= 0;
745 xmax
= (ptr
->nWidth
> 32) ? 32 : ptr
->nWidth
;
746 if (ptr
->nWidth
> 32) {
747 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
748 ptr
->nWidth
, ptr
->nHeight
);
750 ymax
= (ptr
->nHeight
> 32) ? 32 : ptr
->nHeight
;
751 alpha_zero
= check_alpha_zero(ptr
, theImage
);
753 memset(pXorBits
, 0, 128);
754 for (y
=0; y
<ymax
; y
++)
756 for (x
=0; x
<xmax
; x
++)
758 red
= green
= blue
= 0;
759 switch (ptr
->bBitsPerPixel
)
762 theChar
= theImage
[byteIndex
++];
764 theChar
= theImage
[byteIndex
++];
766 theChar
= theImage
[byteIndex
++];
768 theChar
= theImage
[byteIndex
++];
769 /* If the alpha channel is >5% transparent,
770 * assume that we can add it to the bitMask32.
773 *(bitMask32
+ (y
*xmax
+x
)/8) |= 1 << (x
& 7);
776 theChar
= theImage
[byteIndex
++];
778 theChar
= theImage
[byteIndex
++];
780 theChar
= theImage
[byteIndex
++];
784 theChar
= theImage
[byteIndex
++];
785 blue
= theChar
& 0x1F;
786 green
= (theChar
& 0xE0) >> 5;
787 theChar
= theImage
[byteIndex
++];
788 green
|= (theChar
& 0x07) << 3;
789 red
= (theChar
& 0xF8) >> 3;
793 if (red
+green
+blue
> threshold
)
799 pXorBits
[xorIndex
] |= bitShifted
;
813 bitShifted
= bitShifted
<< 1;
816 rscale
= 1 << (16 - rbits
);
817 gscale
= 1 << (16 - gbits
);
818 bscale
= 1 << (16 - bbits
);
821 fg
.red
= rfg
* rscale
/ fgBits
;
822 fg
.green
= gfg
* gscale
/ fgBits
;
823 fg
.blue
= bfg
* bscale
/ fgBits
;
825 else fg
.red
= fg
.green
= fg
.blue
= 0;
826 bgBits
= xmax
* ymax
- fgBits
;
829 bg
.red
= rbg
* rscale
/ bgBits
;
830 bg
.green
= gbg
* gscale
/ bgBits
;
831 bg
.blue
= bbg
* bscale
/ bgBits
;
833 else bg
.red
= bg
.green
= bg
.blue
= 0;
834 pixmapBits
= XCreateBitmapFromData( display
, root_window
, (char *)pXorBits
, xmax
, ymax
);
837 HeapFree( GetProcessHeap(), 0, bitMask32
);
838 XFreePixmap( display
, pixmapAll
);
839 XFreeGC( display
, gc
);
841 XDestroyImage( image
);
846 XPutImage( display
, pixmapAll
, gc
, image
,
847 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
);
848 XSetFunction( display
, gc
, GXcopy
);
850 XCopyArea( display
, pixmapBits
, pixmapAll
, gc
,
851 0, 0, xmax
, ymax
, 0, ptr
->nHeight
);
852 XFreePixmap( display
, pixmapBits
);
855 XDestroyImage( image
);
857 /* Now create the 2 pixmaps for bits and mask */
859 pixmapBits
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
862 pixmapMaskInv
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
863 pixmapMask
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
865 /* Make sure everything went OK so far */
866 if (pixmapBits
&& pixmapMask
&& pixmapMaskInv
)
868 /* We have to do some magic here, as cursors are not fully
869 * compatible between Windows and X11. Under X11, there are
870 * only 3 possible color cursor: black, white and masked. So
871 * we map the 4th Windows color (invert the bits on the screen)
872 * to black and an additional white bit on an other place
873 * (+1,+1). This require some boolean arithmetic:
876 * And Xor Result | Bits Mask Result
877 * 0 0 black | 0 1 background
878 * 0 1 white | 1 1 foreground
879 * 1 0 no change | X 0 no change
880 * 1 1 inverted | 0 1 background
883 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
884 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
886 * FIXME: apparently some servers do support 'inverted' color.
887 * I don't know if it's correct per the X spec, but maybe we
888 * ought to take advantage of it. -- AJ
890 XSetFunction( display
, gc
, GXcopy
);
891 XCopyArea( display
, pixmapAll
, pixmapBits
, gc
,
892 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
893 XCopyArea( display
, pixmapAll
, pixmapMask
, gc
,
894 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
895 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, gc
,
896 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
897 XSetFunction( display
, gc
, GXand
);
898 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, gc
,
899 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
900 XSetFunction( display
, gc
, GXandReverse
);
901 XCopyArea( display
, pixmapAll
, pixmapBits
, gc
,
902 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
903 XSetFunction( display
, gc
, GXorReverse
);
904 XCopyArea( display
, pixmapAll
, pixmapMask
, gc
,
905 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
906 /* Additional white */
907 XSetFunction( display
, gc
, GXor
);
908 XCopyArea( display
, pixmapMaskInv
, pixmapMask
, gc
,
909 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
910 XCopyArea( display
, pixmapMaskInv
, pixmapBits
, gc
,
911 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
912 XSetFunction( display
, gc
, GXcopy
);
917 pixmapMask
= XCreateBitmapFromData( display
, root_window
,
918 bitMask32
, ptr
->nWidth
,
922 /* Make sure hotspot is valid */
923 hotspot
.x
= ptr
->ptHotSpot
.x
;
924 hotspot
.y
= ptr
->ptHotSpot
.y
;
925 if (hotspot
.x
< 0 || hotspot
.x
>= ptr
->nWidth
||
926 hotspot
.y
< 0 || hotspot
.y
>= ptr
->nHeight
)
928 hotspot
.x
= ptr
->nWidth
/ 2;
929 hotspot
.y
= ptr
->nHeight
/ 2;
932 if (pixmapBits
&& pixmapMask
)
933 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapMask
,
934 &fg
, &bg
, hotspot
.x
, hotspot
.y
);
936 /* Now free everything */
938 if (pixmapAll
) XFreePixmap( display
, pixmapAll
);
939 if (pixmapBits
) XFreePixmap( display
, pixmapBits
);
940 if (pixmapMask
) XFreePixmap( display
, pixmapMask
);
941 if (pixmapMaskInv
) XFreePixmap( display
, pixmapMaskInv
);
942 HeapFree( GetProcessHeap(), 0, bitMask32
);
943 XFreeGC( display
, gc
);
949 /***********************************************************************
950 * SetCursor (X11DRV.@)
952 void CDECL
X11DRV_SetCursor( CURSORICONINFO
*lpCursor
)
954 struct x11drv_thread_data
*data
= x11drv_init_thread_data();
958 TRACE("%ux%u, planes %u, bpp %u\n",
959 lpCursor
->nWidth
, lpCursor
->nHeight
, lpCursor
->bPlanes
, lpCursor
->bBitsPerPixel
);
963 /* set the same cursor for all top-level windows of the current thread */
966 cursor
= create_cursor( data
->display
, lpCursor
);
969 if (data
->cursor
) XFreeCursor( data
->display
, data
->cursor
);
970 data
->cursor
= cursor
;
971 if (data
->cursor_window
)
973 XDefineCursor( data
->display
, data
->cursor_window
, cursor
);
974 /* Make the change take effect immediately */
975 XFlush( data
->display
);
981 /***********************************************************************
982 * SetCursorPos (X11DRV.@)
984 BOOL CDECL
X11DRV_SetCursorPos( INT x
, INT y
)
986 Display
*display
= thread_init_display();
989 TRACE( "warping to (%d,%d)\n", x
, y
);
992 if (cursor_pos
.x
== x
&& cursor_pos
.y
== y
)
995 /* We still need to generate WM_MOUSEMOVE */
996 queue_raw_mouse_message( WM_MOUSEMOVE
, NULL
, x
, y
, 0, GetCurrentTime(), 0, 0 );
1001 clip_point_to_rect( &cursor_clip
, &pt
);
1002 XWarpPointer( display
, root_window
, root_window
, 0, 0, 0, 0,
1003 pt
.x
- virtual_screen_rect
.left
, pt
.y
- virtual_screen_rect
.top
);
1004 XFlush( display
); /* avoids bad mouse lag in games that do their own mouse warping */
1006 wine_tsx11_unlock();
1010 /***********************************************************************
1011 * GetCursorPos (X11DRV.@)
1013 BOOL CDECL
X11DRV_GetCursorPos(LPPOINT pos
)
1015 Display
*display
= thread_init_display();
1017 int rootX
, rootY
, winX
, winY
;
1018 unsigned int xstate
;
1021 if ((GetTickCount() - last_time_modified
> 100) &&
1022 XQueryPointer( display
, root_window
, &root
, &child
,
1023 &rootX
, &rootY
, &winX
, &winY
, &xstate
))
1025 update_button_state( xstate
);
1026 winX
+= virtual_screen_rect
.left
;
1027 winY
+= virtual_screen_rect
.top
;
1028 TRACE("pointer at (%d,%d)\n", winX
, winY
);
1029 cursor_pos
.x
= winX
;
1030 cursor_pos
.y
= winY
;
1033 wine_tsx11_unlock();
1038 /***********************************************************************
1039 * ClipCursor (X11DRV.@)
1041 * Set the cursor clipping rectangle.
1043 BOOL CDECL
X11DRV_ClipCursor( LPCRECT clip
)
1045 if (!IntersectRect( &cursor_clip
, &virtual_screen_rect
, clip
))
1046 cursor_clip
= virtual_screen_rect
;
1051 /***********************************************************************
1052 * X11DRV_ButtonPress
1054 void X11DRV_ButtonPress( HWND hwnd
, XEvent
*xev
)
1056 XButtonEvent
*event
= &xev
->xbutton
;
1057 int buttonNum
= event
->button
- 1;
1061 if (buttonNum
>= NB_BUTTONS
) return;
1067 wData
= WHEEL_DELTA
;
1070 wData
= -WHEEL_DELTA
;
1086 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1088 X11DRV_send_mouse_input( hwnd
, button_down_flags
[buttonNum
] | MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_MOVE
,
1089 pt
.x
, pt
.y
, wData
, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1093 /***********************************************************************
1094 * X11DRV_ButtonRelease
1096 void X11DRV_ButtonRelease( HWND hwnd
, XEvent
*xev
)
1098 XButtonEvent
*event
= &xev
->xbutton
;
1099 int buttonNum
= event
->button
- 1;
1103 if (buttonNum
>= NB_BUTTONS
|| !button_up_flags
[buttonNum
]) return;
1122 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1124 X11DRV_send_mouse_input( hwnd
, button_up_flags
[buttonNum
] | MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_MOVE
,
1125 pt
.x
, pt
.y
, wData
, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1129 /***********************************************************************
1130 * X11DRV_MotionNotify
1132 void X11DRV_MotionNotify( HWND hwnd
, XEvent
*xev
)
1134 XMotionEvent
*event
= &xev
->xmotion
;
1137 TRACE("hwnd %p, event->is_hint %d\n", hwnd
, event
->is_hint
);
1141 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1143 X11DRV_send_mouse_input( hwnd
, MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
1144 pt
.x
, pt
.y
, 0, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1148 /***********************************************************************
1149 * X11DRV_EnterNotify
1151 void X11DRV_EnterNotify( HWND hwnd
, XEvent
*xev
)
1153 XCrossingEvent
*event
= &xev
->xcrossing
;
1156 TRACE("hwnd %p, event->detail %d\n", hwnd
, event
->detail
);
1159 if (event
->detail
== NotifyVirtual
|| event
->detail
== NotifyNonlinearVirtual
) return;
1160 if (event
->window
== x11drv_thread_data()->grab_window
) return;
1162 /* simulate a mouse motion event */
1163 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1165 X11DRV_send_mouse_input( hwnd
, MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
1166 pt
.x
, pt
.y
, 0, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );