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 */
91 static XContext cursor_context
;
93 BOOL CDECL
X11DRV_SetCursorPos( INT x
, INT y
);
96 /***********************************************************************
99 * Load the Xcursor library for use.
101 void X11DRV_Xcursor_Init(void)
103 #ifdef SONAME_LIBXCURSOR
104 xcursor_handle
= wine_dlopen(SONAME_LIBXCURSOR
, RTLD_NOW
, NULL
, 0);
105 if (!xcursor_handle
) /* wine_dlopen failed. */
107 WARN("Xcursor failed to load. Using fallback code.\n");
110 #define LOAD_FUNCPTR(f) \
111 p##f = wine_dlsym(xcursor_handle, #f, NULL, 0)
113 LOAD_FUNCPTR(XcursorImageCreate
);
114 LOAD_FUNCPTR(XcursorImageDestroy
);
115 LOAD_FUNCPTR(XcursorImageLoadCursor
);
117 #endif /* SONAME_LIBXCURSOR */
121 /***********************************************************************
124 * get the coordinates of a mouse event
126 static inline void get_coords( HWND hwnd
, Window window
, int x
, int y
, POINT
*pt
)
128 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
132 if (window
== data
->client_window
)
134 pt
->x
= x
+ data
->client_rect
.left
;
135 pt
->y
= y
+ data
->client_rect
.top
;
139 pt
->x
= x
+ data
->whole_rect
.left
;
140 pt
->y
= y
+ data
->whole_rect
.top
;
144 /***********************************************************************
147 * Clip point to the provided rectangle
149 static inline void clip_point_to_rect( LPCRECT rect
, LPPOINT pt
)
151 if (pt
->x
< rect
->left
) pt
->x
= rect
->left
;
152 else if (pt
->x
>= rect
->right
) pt
->x
= rect
->right
- 1;
153 if (pt
->y
< rect
->top
) pt
->y
= rect
->top
;
154 else if (pt
->y
>= rect
->bottom
) pt
->y
= rect
->bottom
- 1;
157 /***********************************************************************
158 * update_button_state
160 * Update the button state with what X provides us
162 static inline void update_button_state( unsigned int state
)
164 key_state_table
[VK_LBUTTON
] = (state
& Button1Mask
? 0x80 : 0);
165 key_state_table
[VK_MBUTTON
] = (state
& Button2Mask
? 0x80 : 0);
166 key_state_table
[VK_RBUTTON
] = (state
& Button3Mask
? 0x80 : 0);
167 /* X-buttons are not reported from XQueryPointer */
170 /***********************************************************************
173 static Cursor
get_empty_cursor(void)
175 static Cursor cursor
;
176 static const char data
[] = { 0 };
183 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
184 pixmap
= XCreateBitmapFromData( gdi_display
, root_window
, data
, 1, 1 );
187 cursor
= XCreatePixmapCursor( gdi_display
, pixmap
, pixmap
, &bg
, &bg
, 0, 0 );
188 XFreePixmap( gdi_display
, pixmap
);
194 /***********************************************************************
197 Cursor
get_x11_cursor( HCURSOR handle
)
201 if (!handle
) return get_empty_cursor();
203 if (cursor_context
&& !XFindContext( gdi_display
, (XID
)handle
, cursor_context
, (char **)&cursor
))
208 /***********************************************************************
211 * Update the various window states on a mouse event.
213 static void update_mouse_state( HWND hwnd
, Window window
, int x
, int y
, unsigned int state
, POINT
*pt
)
215 struct x11drv_thread_data
*data
= x11drv_thread_data();
217 get_coords( hwnd
, window
, x
, y
, pt
);
219 data
->cursor_window
= hwnd
;
221 /* update the wine server Z-order */
223 if (window
!= data
->grab_window
&&
224 /* ignore event if a button is pressed, since the mouse is then grabbed too */
225 !(state
& (Button1Mask
|Button2Mask
|Button3Mask
|Button4Mask
|Button5Mask
|Button6Mask
|Button7Mask
)))
227 SERVER_START_REQ( update_window_zorder
)
229 req
->window
= wine_server_user_handle( hwnd
);
230 req
->rect
.left
= pt
->x
;
231 req
->rect
.top
= pt
->y
;
232 req
->rect
.right
= pt
->x
+ 1;
233 req
->rect
.bottom
= pt
->y
+ 1;
234 wine_server_call( req
);
241 /***********************************************************************
244 static WORD
get_key_state(void)
248 if (GetSystemMetrics( SM_SWAPBUTTON
))
250 if (key_state_table
[VK_RBUTTON
] & 0x80) ret
|= MK_LBUTTON
;
251 if (key_state_table
[VK_LBUTTON
] & 0x80) ret
|= MK_RBUTTON
;
255 if (key_state_table
[VK_LBUTTON
] & 0x80) ret
|= MK_LBUTTON
;
256 if (key_state_table
[VK_RBUTTON
] & 0x80) ret
|= MK_RBUTTON
;
258 if (key_state_table
[VK_MBUTTON
] & 0x80) ret
|= MK_MBUTTON
;
259 if (key_state_table
[VK_SHIFT
] & 0x80) ret
|= MK_SHIFT
;
260 if (key_state_table
[VK_CONTROL
] & 0x80) ret
|= MK_CONTROL
;
261 if (key_state_table
[VK_XBUTTON1
] & 0x80) ret
|= MK_XBUTTON1
;
262 if (key_state_table
[VK_XBUTTON2
] & 0x80) ret
|= MK_XBUTTON2
;
267 /***********************************************************************
268 * queue_raw_mouse_message
270 static void queue_raw_mouse_message( UINT message
, HWND hwnd
, DWORD x
, DWORD y
,
271 DWORD data
, DWORD time
, DWORD extra_info
, UINT injected_flags
)
278 hook
.mouseData
= MAKELONG( 0, data
);
279 hook
.flags
= injected_flags
;
281 hook
.dwExtraInfo
= extra_info
;
283 last_time_modified
= GetTickCount();
284 if (HOOK_CallHooks( WH_MOUSE_LL
, HC_ACTION
, message
, (LPARAM
)&hook
, TRUE
)) return;
286 SERVER_START_REQ( send_hardware_message
)
288 req
->id
= (injected_flags
& LLMHF_INJECTED
) ? 0 : GetCurrentThreadId();
289 req
->win
= wine_server_user_handle( hwnd
);
291 req
->wparam
= MAKEWPARAM( get_key_state(), data
);
296 req
->info
= extra_info
;
297 wine_server_call( req
);
298 cursor
= (reply
->count
>= 0) ? wine_server_ptr_handle(reply
->cursor
) : 0;
304 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
305 if (data
&& cursor
!= data
->cursor
)
307 Cursor xcursor
= get_x11_cursor( cursor
);
308 if (xcursor
) XDefineCursor( gdi_display
, data
->whole_window
, xcursor
);
309 data
->cursor
= cursor
;
315 /***********************************************************************
316 * X11DRV_send_mouse_input
318 void X11DRV_send_mouse_input( HWND hwnd
, DWORD flags
, DWORD x
, DWORD y
,
319 DWORD data
, DWORD time
, DWORD extra_info
, UINT injected_flags
)
323 if (flags
& MOUSEEVENTF_MOVE
&& flags
& MOUSEEVENTF_ABSOLUTE
)
325 if (injected_flags
& LLMHF_INJECTED
)
327 pt
.x
= (x
* screen_width
) >> 16;
328 pt
.y
= (y
* screen_height
) >> 16;
335 if (cursor_pos
.x
== x
&& cursor_pos
.y
== y
&&
336 (flags
& ~(MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
)))
337 flags
&= ~MOUSEEVENTF_MOVE
;
341 else if (flags
& MOUSEEVENTF_MOVE
)
343 int accel
[3], xMult
= 1, yMult
= 1;
345 /* dx and dy can be negative numbers for relative movements */
346 SystemParametersInfoW(SPI_GETMOUSE
, 0, accel
, 0);
348 if (abs(x
) > accel
[0] && accel
[2] != 0)
351 if ((abs(x
) > accel
[1]) && (accel
[2] == 2)) xMult
= 4;
353 if (abs(y
) > accel
[0] && accel
[2] != 0)
356 if ((abs(y
) > accel
[1]) && (accel
[2] == 2)) yMult
= 4;
360 pt
.x
= cursor_pos
.x
+ (long)x
* xMult
;
361 pt
.y
= cursor_pos
.y
+ (long)y
* yMult
;
371 if (flags
& MOUSEEVENTF_MOVE
)
373 queue_raw_mouse_message( WM_MOUSEMOVE
, hwnd
, pt
.x
, pt
.y
, data
, time
,
374 extra_info
, injected_flags
);
375 if ((injected_flags
& LLMHF_INJECTED
) &&
376 ((flags
& MOUSEEVENTF_ABSOLUTE
) || x
|| y
)) /* we have to actually move the cursor */
378 X11DRV_SetCursorPos( pt
.x
, pt
.y
);
383 clip_point_to_rect( &cursor_clip
, &pt
);
388 if (flags
& MOUSEEVENTF_LEFTDOWN
)
390 key_state_table
[VK_LBUTTON
] |= 0xc0;
391 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON
) ? WM_RBUTTONDOWN
: WM_LBUTTONDOWN
,
392 hwnd
, pt
.x
, pt
.y
, data
, time
, extra_info
, injected_flags
);
394 if (flags
& MOUSEEVENTF_LEFTUP
)
396 key_state_table
[VK_LBUTTON
] &= ~0x80;
397 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON
) ? WM_RBUTTONUP
: WM_LBUTTONUP
,
398 hwnd
, pt
.x
, pt
.y
, data
, time
, extra_info
, injected_flags
);
400 if (flags
& MOUSEEVENTF_RIGHTDOWN
)
402 key_state_table
[VK_RBUTTON
] |= 0xc0;
403 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON
) ? WM_LBUTTONDOWN
: WM_RBUTTONDOWN
,
404 hwnd
, pt
.x
, pt
.y
, data
, time
, extra_info
, injected_flags
);
406 if (flags
& MOUSEEVENTF_RIGHTUP
)
408 key_state_table
[VK_RBUTTON
] &= ~0x80;
409 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON
) ? WM_LBUTTONUP
: WM_RBUTTONUP
,
410 hwnd
, pt
.x
, pt
.y
, data
, time
, extra_info
, injected_flags
);
412 if (flags
& MOUSEEVENTF_MIDDLEDOWN
)
414 key_state_table
[VK_MBUTTON
] |= 0xc0;
415 queue_raw_mouse_message( WM_MBUTTONDOWN
, hwnd
, pt
.x
, pt
.y
, data
, time
,
416 extra_info
, injected_flags
);
418 if (flags
& MOUSEEVENTF_MIDDLEUP
)
420 key_state_table
[VK_MBUTTON
] &= ~0x80;
421 queue_raw_mouse_message( WM_MBUTTONUP
, hwnd
, pt
.x
, pt
.y
, data
, time
,
422 extra_info
, injected_flags
);
424 if (flags
& MOUSEEVENTF_WHEEL
)
426 queue_raw_mouse_message( WM_MOUSEWHEEL
, hwnd
, pt
.x
, pt
.y
, data
, time
,
427 extra_info
, injected_flags
);
429 if (flags
& MOUSEEVENTF_XDOWN
)
431 key_state_table
[VK_XBUTTON1
+ data
- 1] |= 0xc0;
432 queue_raw_mouse_message( WM_XBUTTONDOWN
, hwnd
, pt
.x
, pt
.y
, data
, time
,
433 extra_info
, injected_flags
);
435 if (flags
& MOUSEEVENTF_XUP
)
437 key_state_table
[VK_XBUTTON1
+ data
- 1] &= ~0x80;
438 queue_raw_mouse_message( WM_XBUTTONUP
, hwnd
, pt
.x
, pt
.y
, data
, time
,
439 extra_info
, injected_flags
);
444 /***********************************************************************
447 * Generally 32 bit bitmaps have an alpha channel which is used in favor of the
448 * AND mask. However, if all pixels have alpha = 0x00, the bitmap is treated
449 * like one without alpha and the masks are used. As soon as one pixel has
450 * alpha != 0x00, and the mask ignored as described in the docs.
452 * This is most likely for applications which create the bitmaps with
453 * CreateDIBitmap, which creates a device dependent bitmap, so the format that
454 * arrives when loading depends on the screen's bpp. Apps that were written at
455 * 8 / 16 bpp times do not know about the 32 bit alpha, so they would get a
456 * completely transparent cursor on 32 bit displays.
458 * Non-32 bit bitmaps always use the AND mask.
460 static BOOL
check_alpha_zero(CURSORICONINFO
*ptr
, unsigned char *xor_bits
)
463 unsigned char *xor_ptr
;
465 if (ptr
->bBitsPerPixel
== 32)
467 for (y
= 0; y
< ptr
->nHeight
; ++y
)
469 xor_ptr
= xor_bits
+ (y
* ptr
->nWidthBytes
);
470 for (x
= 0; x
< ptr
->nWidth
; ++x
)
472 if (xor_ptr
[3] != 0x00)
485 #ifdef SONAME_LIBXCURSOR
487 /***********************************************************************
488 * create_cursor_image
490 * Create an XcursorImage from a CURSORICONINFO
492 static XcursorImage
*create_cursor_image( CURSORICONINFO
*ptr
)
494 static const unsigned char convert_5to8
[] =
496 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
497 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
498 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
499 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
501 static const unsigned char convert_6to8
[] =
503 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
504 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
505 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
506 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
507 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
508 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
509 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
510 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
515 unsigned char *and_bits
, *and_ptr
, *xor_bits
, *xor_ptr
;
516 int and_width_bytes
, xor_width_bytes
;
517 XcursorPixel
*pixel_ptr
;
522 and_width_bytes
= 2 * ((ptr
->nWidth
+15) / 16);
523 xor_width_bytes
= ptr
->nWidthBytes
;
525 and_size
= ptr
->nHeight
* and_width_bytes
;
526 and_ptr
= and_bits
= (unsigned char *)(ptr
+ 1);
528 xor_ptr
= xor_bits
= and_ptr
+ and_size
;
530 image
= pXcursorImageCreate( ptr
->nWidth
, ptr
->nHeight
);
531 if (!image
) return NULL
;
533 pixel_ptr
= image
->pixels
;
535 alpha_zero
= check_alpha_zero(ptr
, xor_bits
);
537 /* On windows, to calculate the color for a pixel, first an AND is done
538 * with the background and the "and" bitmap, then an XOR with the "xor"
539 * bitmap. This means that when the data in the "and" bitmap is 0, the
540 * pixel will get the color as specified in the "xor" bitmap.
541 * However, if the data in the "and" bitmap is 1, the result will be the
542 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
543 * data is completely black (0x000000) the pixel will become transparent,
544 * in case it's white (0xffffff) the pixel will become the inverse of the
547 * Since we can't support inverting colors, we map the grayscale value of
548 * the "xor" data to the alpha channel, and xor the color with either
551 for (y
= 0; y
< ptr
->nHeight
; ++y
)
553 and_ptr
= and_bits
+ (y
* and_width_bytes
);
554 xor_ptr
= xor_bits
+ (y
* xor_width_bytes
);
556 for (x
= 0; x
< ptr
->nWidth
; ++x
)
558 /* Xcursor pixel data is in ARGB format, with A in the high byte */
559 switch (ptr
->bBitsPerPixel
)
562 /* BGRA, 8 bits each */
563 *pixel_ptr
= *xor_ptr
++;
564 *pixel_ptr
|= *xor_ptr
++ << 8;
565 *pixel_ptr
|= *xor_ptr
++ << 16;
566 *pixel_ptr
|= *xor_ptr
++ << 24;
570 /* BGR, 8 bits each */
571 *pixel_ptr
= *xor_ptr
++;
572 *pixel_ptr
|= *xor_ptr
++ << 8;
573 *pixel_ptr
|= *xor_ptr
++ << 16;
577 /* BGR, 5 red, 6 green, 5 blue */
578 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
579 *pixel_ptr
= convert_5to8
[*xor_ptr
& 0x1f];
580 tmp
= (*xor_ptr
++ & 0xe0) >> 5;
581 tmp
|= (*xor_ptr
& 0x07) << 3;
582 *pixel_ptr
|= convert_6to8
[tmp
] << 16;
583 *pixel_ptr
|= convert_5to8
[*xor_ptr
++ >> 3] << 24;
587 if (*xor_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
= 0xffffff;
589 if ((x
& 7) == 7) ++xor_ptr
;
593 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr
->bBitsPerPixel
);
600 if (~*and_ptr
& (1 << (7 - (x
& 7)))) *pixel_ptr
|= 0xff << 24;
603 int alpha
= (*pixel_ptr
& 0xff) * 0.30f
604 + ((*pixel_ptr
& 0xff00) >> 8) * 0.55f
605 + ((*pixel_ptr
& 0xff0000) >> 16) * 0.15f
;
606 *pixel_ptr
^= ((x
+ y
) % 2) ? 0xffffff : 0x000000;
607 *pixel_ptr
|= alpha
<< 24;
609 if ((x
& 7) == 7) ++and_ptr
;
619 /***********************************************************************
620 * create_xcursor_cursor
622 * Use Xcursor to create an X cursor from a Windows one.
624 static Cursor
create_xcursor_cursor( Display
*display
, CURSORICONINFO
*ptr
)
629 image
= create_cursor_image( ptr
);
630 if (!image
) return 0;
632 /* Make sure hotspot is valid */
633 image
->xhot
= ptr
->ptHotSpot
.x
;
634 image
->yhot
= ptr
->ptHotSpot
.y
;
635 if (image
->xhot
>= image
->width
||
636 image
->yhot
>= image
->height
)
638 image
->xhot
= image
->width
/ 2;
639 image
->yhot
= image
->height
/ 2;
644 cursor
= pXcursorImageLoadCursor( display
, image
);
645 pXcursorImageDestroy( image
);
650 #endif /* SONAME_LIBXCURSOR */
653 /***********************************************************************
656 * Create an X cursor from a Windows one.
658 static Cursor
create_cursor( Display
*display
, CURSORICONINFO
*ptr
)
660 Pixmap pixmapBits
, pixmapMask
, pixmapMaskInv
= 0, pixmapAll
;
662 Cursor cursor
= None
;
664 char *bitMask32
= NULL
;
665 BOOL alpha_zero
= TRUE
;
667 if (!ptr
) return get_empty_cursor();
669 #ifdef SONAME_LIBXCURSOR
670 if (pXcursorImageLoadCursor
) return create_xcursor_cursor( display
, ptr
);
673 /* Create the X cursor from the bits */
678 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
679 ptr
->nWidth
, ptr
->nHeight
, ptr
->bPlanes
, ptr
->bBitsPerPixel
,
682 /* Create a pixmap and transfer all the bits to it */
684 /* NOTE: Following hack works, but only because XFree depth
685 * 1 images really use 1 bit/pixel (and so the same layout
686 * as the Windows cursor data). Perhaps use a more generic
689 /* This pixmap will be written with two bitmaps. The first is
690 * the mask and the second is the image.
692 if (!(pixmapAll
= XCreatePixmap( display
, root_window
,
693 ptr
->nWidth
, ptr
->nHeight
* 2, 1 )))
695 if (!(image
= XCreateImage( display
, visual
,
696 1, ZPixmap
, 0, (char *)(ptr
+ 1), ptr
->nWidth
,
697 ptr
->nHeight
* 2, 16, ptr
->nWidthBytes
/ptr
->bBitsPerPixel
)))
699 XFreePixmap( display
, pixmapAll
);
702 gc
= XCreateGC( display
, pixmapAll
, 0, NULL
);
703 XSetGraphicsExposures( display
, gc
, False
);
704 image
->byte_order
= MSBFirst
;
705 image
->bitmap_bit_order
= MSBFirst
;
706 image
->bitmap_unit
= 16;
707 _XInitImageFuncPtrs(image
);
708 if (ptr
->bPlanes
* ptr
->bBitsPerPixel
== 1)
710 /* A plain old white on black cursor. */
711 fg
.red
= fg
.green
= fg
.blue
= 0xffff;
712 bg
.red
= bg
.green
= bg
.blue
= 0x0000;
713 XPutImage( display
, pixmapAll
, gc
, image
,
714 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
* 2 );
718 int rbits
, gbits
, bbits
, red
, green
, blue
;
719 int rfg
, gfg
, bfg
, rbg
, gbg
, bbg
;
720 int rscale
, gscale
, bscale
;
721 int x
, y
, xmax
, ymax
, byteIndex
, xorIndex
;
722 unsigned char *theMask
, *theImage
, theChar
;
723 int threshold
, fgBits
, bgBits
, bitShifted
;
724 BYTE pXorBits
[128]; /* Up to 32x32 icons */
726 switch (ptr
->bBitsPerPixel
)
729 bitMask32
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
,
730 ptr
->nWidth
* ptr
->nHeight
/ 8 );
745 FIXME("Currently no support for cursors with %d bits per pixel\n",
747 XFreePixmap( display
, pixmapAll
);
748 XFreeGC( display
, gc
);
750 XDestroyImage( image
);
753 /* The location of the mask. */
754 theMask
= (unsigned char *)(ptr
+ 1);
755 /* The mask should still be 1 bit per pixel. The color image
756 * should immediately follow the mask.
758 theImage
= &theMask
[ptr
->nWidth
/8 * ptr
->nHeight
];
759 rfg
= gfg
= bfg
= rbg
= gbg
= bbg
= 0;
764 xmax
= (ptr
->nWidth
> 32) ? 32 : ptr
->nWidth
;
765 if (ptr
->nWidth
> 32) {
766 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
767 ptr
->nWidth
, ptr
->nHeight
);
769 ymax
= (ptr
->nHeight
> 32) ? 32 : ptr
->nHeight
;
770 alpha_zero
= check_alpha_zero(ptr
, theImage
);
772 memset(pXorBits
, 0, 128);
773 for (y
=0; y
<ymax
; y
++)
775 for (x
=0; x
<xmax
; x
++)
777 red
= green
= blue
= 0;
778 switch (ptr
->bBitsPerPixel
)
781 theChar
= theImage
[byteIndex
++];
783 theChar
= theImage
[byteIndex
++];
785 theChar
= theImage
[byteIndex
++];
787 theChar
= theImage
[byteIndex
++];
788 /* If the alpha channel is >5% transparent,
789 * assume that we can add it to the bitMask32.
792 *(bitMask32
+ (y
*xmax
+x
)/8) |= 1 << (x
& 7);
795 theChar
= theImage
[byteIndex
++];
797 theChar
= theImage
[byteIndex
++];
799 theChar
= theImage
[byteIndex
++];
803 theChar
= theImage
[byteIndex
++];
804 blue
= theChar
& 0x1F;
805 green
= (theChar
& 0xE0) >> 5;
806 theChar
= theImage
[byteIndex
++];
807 green
|= (theChar
& 0x07) << 3;
808 red
= (theChar
& 0xF8) >> 3;
812 if (red
+green
+blue
> threshold
)
818 pXorBits
[xorIndex
] |= bitShifted
;
832 bitShifted
= bitShifted
<< 1;
835 rscale
= 1 << (16 - rbits
);
836 gscale
= 1 << (16 - gbits
);
837 bscale
= 1 << (16 - bbits
);
840 fg
.red
= rfg
* rscale
/ fgBits
;
841 fg
.green
= gfg
* gscale
/ fgBits
;
842 fg
.blue
= bfg
* bscale
/ fgBits
;
844 else fg
.red
= fg
.green
= fg
.blue
= 0;
845 bgBits
= xmax
* ymax
- fgBits
;
848 bg
.red
= rbg
* rscale
/ bgBits
;
849 bg
.green
= gbg
* gscale
/ bgBits
;
850 bg
.blue
= bbg
* bscale
/ bgBits
;
852 else bg
.red
= bg
.green
= bg
.blue
= 0;
853 pixmapBits
= XCreateBitmapFromData( display
, root_window
, (char *)pXorBits
, xmax
, ymax
);
856 HeapFree( GetProcessHeap(), 0, bitMask32
);
857 XFreePixmap( display
, pixmapAll
);
858 XFreeGC( display
, gc
);
860 XDestroyImage( image
);
865 XPutImage( display
, pixmapAll
, gc
, image
,
866 0, 0, 0, 0, ptr
->nWidth
, ptr
->nHeight
);
867 XSetFunction( display
, gc
, GXcopy
);
869 XCopyArea( display
, pixmapBits
, pixmapAll
, gc
,
870 0, 0, xmax
, ymax
, 0, ptr
->nHeight
);
871 XFreePixmap( display
, pixmapBits
);
874 XDestroyImage( image
);
876 /* Now create the 2 pixmaps for bits and mask */
878 pixmapBits
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
881 pixmapMaskInv
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
882 pixmapMask
= XCreatePixmap( display
, root_window
, ptr
->nWidth
, ptr
->nHeight
, 1 );
884 /* Make sure everything went OK so far */
885 if (pixmapBits
&& pixmapMask
&& pixmapMaskInv
)
887 /* We have to do some magic here, as cursors are not fully
888 * compatible between Windows and X11. Under X11, there are
889 * only 3 possible color cursor: black, white and masked. So
890 * we map the 4th Windows color (invert the bits on the screen)
891 * to black and an additional white bit on an other place
892 * (+1,+1). This require some boolean arithmetic:
895 * And Xor Result | Bits Mask Result
896 * 0 0 black | 0 1 background
897 * 0 1 white | 1 1 foreground
898 * 1 0 no change | X 0 no change
899 * 1 1 inverted | 0 1 background
902 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
903 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
905 * FIXME: apparently some servers do support 'inverted' color.
906 * I don't know if it's correct per the X spec, but maybe we
907 * ought to take advantage of it. -- AJ
909 XSetFunction( display
, gc
, GXcopy
);
910 XCopyArea( display
, pixmapAll
, pixmapBits
, gc
,
911 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
912 XCopyArea( display
, pixmapAll
, pixmapMask
, gc
,
913 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
914 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, gc
,
915 0, 0, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
916 XSetFunction( display
, gc
, GXand
);
917 XCopyArea( display
, pixmapAll
, pixmapMaskInv
, gc
,
918 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
919 XSetFunction( display
, gc
, GXandReverse
);
920 XCopyArea( display
, pixmapAll
, pixmapBits
, gc
,
921 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
922 XSetFunction( display
, gc
, GXorReverse
);
923 XCopyArea( display
, pixmapAll
, pixmapMask
, gc
,
924 0, ptr
->nHeight
, ptr
->nWidth
, ptr
->nHeight
, 0, 0 );
925 /* Additional white */
926 XSetFunction( display
, gc
, GXor
);
927 XCopyArea( display
, pixmapMaskInv
, pixmapMask
, gc
,
928 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
929 XCopyArea( display
, pixmapMaskInv
, pixmapBits
, gc
,
930 0, 0, ptr
->nWidth
, ptr
->nHeight
, 1, 1 );
931 XSetFunction( display
, gc
, GXcopy
);
936 pixmapMask
= XCreateBitmapFromData( display
, root_window
,
937 bitMask32
, ptr
->nWidth
,
941 /* Make sure hotspot is valid */
942 hotspot
.x
= ptr
->ptHotSpot
.x
;
943 hotspot
.y
= ptr
->ptHotSpot
.y
;
944 if (hotspot
.x
< 0 || hotspot
.x
>= ptr
->nWidth
||
945 hotspot
.y
< 0 || hotspot
.y
>= ptr
->nHeight
)
947 hotspot
.x
= ptr
->nWidth
/ 2;
948 hotspot
.y
= ptr
->nHeight
/ 2;
951 if (pixmapBits
&& pixmapMask
)
952 cursor
= XCreatePixmapCursor( display
, pixmapBits
, pixmapMask
,
953 &fg
, &bg
, hotspot
.x
, hotspot
.y
);
955 /* Now free everything */
957 if (pixmapAll
) XFreePixmap( display
, pixmapAll
);
958 if (pixmapBits
) XFreePixmap( display
, pixmapBits
);
959 if (pixmapMask
) XFreePixmap( display
, pixmapMask
);
960 if (pixmapMaskInv
) XFreePixmap( display
, pixmapMaskInv
);
961 HeapFree( GetProcessHeap(), 0, bitMask32
);
962 XFreeGC( display
, gc
);
967 /***********************************************************************
968 * CreateCursorIcon (X11DRV.@)
970 void CDECL
X11DRV_CreateCursorIcon( HCURSOR handle
, CURSORICONINFO
*info
)
972 static const WORD ICON_HOTSPOT
= 0x4242;
975 /* ignore icons (FIXME: shouldn't use magic hotspot value) */
976 if (info
->ptHotSpot
.x
== ICON_HOTSPOT
&& info
->ptHotSpot
.y
== ICON_HOTSPOT
) return;
979 cursor
= create_cursor( gdi_display
, info
);
982 if (!cursor_context
) cursor_context
= XUniqueContext();
983 XSaveContext( gdi_display
, (XID
)handle
, cursor_context
, (char *)cursor
);
984 TRACE( "cursor %p %ux%u, planes %u, bpp %u -> xid %lx\n",
985 handle
, info
->nWidth
, info
->nHeight
, info
->bPlanes
, info
->bBitsPerPixel
, cursor
);
990 /***********************************************************************
991 * DestroyCursorIcon (X11DRV.@)
993 void CDECL
X11DRV_DestroyCursorIcon( HCURSOR handle
)
998 if ((cursor
= get_x11_cursor( handle
)))
1000 TRACE( "%p xid %lx\n", handle
, cursor
);
1001 XFreeCursor( gdi_display
, cursor
);
1002 XDeleteContext( gdi_display
, (XID
)handle
, cursor_context
);
1004 wine_tsx11_unlock();
1007 /***********************************************************************
1008 * SetCursor (X11DRV.@)
1010 void CDECL
X11DRV_SetCursor( HCURSOR handle
)
1012 struct x11drv_thread_data
*thread_data
= x11drv_init_thread_data();
1013 struct x11drv_win_data
*data
;
1016 if (!(data
= X11DRV_get_win_data( thread_data
->cursor_window
))) return;
1019 if ((cursor
= get_x11_cursor( handle
)))
1021 TRACE( "%p xid %lx\n", handle
, cursor
);
1022 XDefineCursor( gdi_display
, data
->whole_window
, cursor
);
1023 /* Make the change take effect immediately */
1024 XFlush( gdi_display
);
1025 data
->cursor
= handle
;
1027 wine_tsx11_unlock();
1030 /***********************************************************************
1031 * SetCursorPos (X11DRV.@)
1033 BOOL CDECL
X11DRV_SetCursorPos( INT x
, INT y
)
1035 Display
*display
= thread_init_display();
1038 TRACE( "warping to (%d,%d)\n", x
, y
);
1041 if (cursor_pos
.x
== x
&& cursor_pos
.y
== y
)
1043 wine_tsx11_unlock();
1044 /* We still need to generate WM_MOUSEMOVE */
1045 queue_raw_mouse_message( WM_MOUSEMOVE
, NULL
, x
, y
, 0, GetCurrentTime(), 0, 0 );
1050 clip_point_to_rect( &cursor_clip
, &pt
);
1051 XWarpPointer( display
, root_window
, root_window
, 0, 0, 0, 0,
1052 pt
.x
- virtual_screen_rect
.left
, pt
.y
- virtual_screen_rect
.top
);
1053 XFlush( display
); /* avoids bad mouse lag in games that do their own mouse warping */
1055 wine_tsx11_unlock();
1059 /***********************************************************************
1060 * GetCursorPos (X11DRV.@)
1062 BOOL CDECL
X11DRV_GetCursorPos(LPPOINT pos
)
1064 Display
*display
= thread_init_display();
1066 int rootX
, rootY
, winX
, winY
;
1067 unsigned int xstate
;
1070 if ((GetTickCount() - last_time_modified
> 100) &&
1071 XQueryPointer( display
, root_window
, &root
, &child
,
1072 &rootX
, &rootY
, &winX
, &winY
, &xstate
))
1074 update_button_state( xstate
);
1075 winX
+= virtual_screen_rect
.left
;
1076 winY
+= virtual_screen_rect
.top
;
1077 TRACE("pointer at (%d,%d)\n", winX
, winY
);
1078 cursor_pos
.x
= winX
;
1079 cursor_pos
.y
= winY
;
1082 wine_tsx11_unlock();
1087 /***********************************************************************
1088 * ClipCursor (X11DRV.@)
1090 * Set the cursor clipping rectangle.
1092 BOOL CDECL
X11DRV_ClipCursor( LPCRECT clip
)
1094 if (!IntersectRect( &cursor_clip
, &virtual_screen_rect
, clip
))
1095 cursor_clip
= virtual_screen_rect
;
1100 /***********************************************************************
1101 * X11DRV_ButtonPress
1103 void X11DRV_ButtonPress( HWND hwnd
, XEvent
*xev
)
1105 XButtonEvent
*event
= &xev
->xbutton
;
1106 int buttonNum
= event
->button
- 1;
1110 if (buttonNum
>= NB_BUTTONS
) return;
1116 wData
= WHEEL_DELTA
;
1119 wData
= -WHEEL_DELTA
;
1135 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1137 X11DRV_send_mouse_input( hwnd
, button_down_flags
[buttonNum
] | MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_MOVE
,
1138 pt
.x
, pt
.y
, wData
, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1142 /***********************************************************************
1143 * X11DRV_ButtonRelease
1145 void X11DRV_ButtonRelease( HWND hwnd
, XEvent
*xev
)
1147 XButtonEvent
*event
= &xev
->xbutton
;
1148 int buttonNum
= event
->button
- 1;
1152 if (buttonNum
>= NB_BUTTONS
|| !button_up_flags
[buttonNum
]) return;
1171 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1173 X11DRV_send_mouse_input( hwnd
, button_up_flags
[buttonNum
] | MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_MOVE
,
1174 pt
.x
, pt
.y
, wData
, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1178 /***********************************************************************
1179 * X11DRV_MotionNotify
1181 void X11DRV_MotionNotify( HWND hwnd
, XEvent
*xev
)
1183 XMotionEvent
*event
= &xev
->xmotion
;
1186 TRACE("hwnd %p, event->is_hint %d\n", hwnd
, event
->is_hint
);
1190 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1192 X11DRV_send_mouse_input( hwnd
, MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
1193 pt
.x
, pt
.y
, 0, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );
1197 /***********************************************************************
1198 * X11DRV_EnterNotify
1200 void X11DRV_EnterNotify( HWND hwnd
, XEvent
*xev
)
1202 XCrossingEvent
*event
= &xev
->xcrossing
;
1205 TRACE("hwnd %p, event->detail %d\n", hwnd
, event
->detail
);
1208 if (event
->detail
== NotifyVirtual
|| event
->detail
== NotifyNonlinearVirtual
) return;
1209 if (event
->window
== x11drv_thread_data()->grab_window
) return;
1211 /* simulate a mouse motion event */
1212 update_mouse_state( hwnd
, event
->window
, event
->x
, event
->y
, event
->state
, &pt
);
1214 X11DRV_send_mouse_input( hwnd
, MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
,
1215 pt
.x
, pt
.y
, 0, EVENT_x11_time_to_win32_time(event
->time
), 0, 0 );