winex11.drv: Fix color conversion for 16 bpp cursors.
[wine/wine64.git] / dlls / winex11.drv / mouse.c
blob89587d8f75d23e29cf13cc522c4341db5ba37b86
1 /*
2 * X11 mouse driver
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <X11/Xlib.h>
26 #include <stdarg.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);
35 # undef MAKE_FUNCPTR
36 #endif /* SONAME_LIBXCURSOR */
38 #define NONAMELESSUNION
39 #define NONAMELESSSTRUCT
40 #include "windef.h"
41 #include "winbase.h"
42 #include "wine/winuser16.h"
44 #include "x11drv.h"
45 #include "wine/server.h"
46 #include "wine/library.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
51 /**********************************************************************/
53 #ifndef Button6Mask
54 #define Button6Mask (1<<13)
55 #endif
56 #ifndef Button7Mask
57 #define Button7Mask (1<<14)
58 #endif
60 #define NB_BUTTONS 9 /* Windows can handle 5 buttons and the wheel too */
62 static const UINT button_down_flags[NB_BUTTONS] =
64 MOUSEEVENTF_LEFTDOWN,
65 MOUSEEVENTF_MIDDLEDOWN,
66 MOUSEEVENTF_RIGHTDOWN,
67 MOUSEEVENTF_WHEEL,
68 MOUSEEVENTF_WHEEL,
69 MOUSEEVENTF_XDOWN, /* FIXME: horizontal wheel */
70 MOUSEEVENTF_XDOWN,
71 MOUSEEVENTF_XDOWN,
72 MOUSEEVENTF_XDOWN
75 static const UINT button_up_flags[NB_BUTTONS] =
77 MOUSEEVENTF_LEFTUP,
78 MOUSEEVENTF_MIDDLEUP,
79 MOUSEEVENTF_RIGHTUP,
82 MOUSEEVENTF_XUP,
83 MOUSEEVENTF_XUP,
84 MOUSEEVENTF_XUP,
85 MOUSEEVENTF_XUP
88 POINT cursor_pos;
89 static DWORD last_time_modified;
90 static RECT cursor_clip; /* Cursor clipping rect */
92 BOOL X11DRV_SetCursorPos( INT x, INT y );
95 /***********************************************************************
96 * X11DRV_Xcursor_Init
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");
107 return;
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);
115 #undef LOAD_FUNCPTR
116 #endif /* SONAME_LIBXCURSOR */
120 /***********************************************************************
121 * get_coords
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 );
129 if (!data) return;
131 if (window == data->client_window)
133 pt->x = x + data->client_rect.left;
134 pt->y = y + data->client_rect.top;
136 else
138 pt->x = x + data->whole_rect.left;
139 pt->y = y + data->whole_rect.top;
143 /***********************************************************************
144 * clip_point_to_rect
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 /***********************************************************************
171 * update_mouse_state
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;
186 wine_tsx11_lock();
187 if (data->cursor) XDefineCursor( data->display, window, data->cursor );
188 wine_tsx11_unlock();
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 );
206 SERVER_END_REQ;
211 /***********************************************************************
212 * get_key_state
214 static WORD get_key_state(void)
216 WORD ret = 0;
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;
223 else
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;
233 return ret;
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 )
243 MSLLHOOKSTRUCT hook;
245 hook.pt.x = x;
246 hook.pt.y = y;
247 hook.mouseData = MAKELONG( 0, data );
248 hook.flags = injected_flags;
249 hook.time = time;
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 );
259 req->msg = message;
260 req->wparam = MAKEWPARAM( get_key_state(), data );
261 req->lparam = 0;
262 req->x = x;
263 req->y = y;
264 req->time = time;
265 req->info = extra_info;
266 wine_server_call( req );
268 SERVER_END_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 )
279 POINT pt;
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;
288 else
290 pt.x = x;
291 pt.y = y;
292 wine_tsx11_lock();
293 if (cursor_pos.x == x && cursor_pos.y == y &&
294 (flags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE)))
295 flags &= ~MOUSEEVENTF_MOVE;
296 wine_tsx11_unlock();
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)
308 xMult = 2;
309 if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4;
311 if (abs(y) > accel[0] && accel[2] != 0)
313 yMult = 2;
314 if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4;
317 wine_tsx11_lock();
318 pt.x = cursor_pos.x + (long)x * xMult;
319 pt.y = cursor_pos.y + (long)y * yMult;
320 wine_tsx11_unlock();
322 else
324 wine_tsx11_lock();
325 pt = cursor_pos;
326 wine_tsx11_unlock();
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 );
338 else
340 wine_tsx11_lock();
341 clip_point_to_rect( &cursor_clip, &pt);
342 cursor_pos = pt;
343 wine_tsx11_unlock();
346 if (flags & MOUSEEVENTF_LEFTDOWN)
348 key_state_table[VK_LBUTTON] |= 0xc0;
349 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
350 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
352 if (flags & MOUSEEVENTF_LEFTUP)
354 key_state_table[VK_LBUTTON] &= ~0x80;
355 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_RBUTTONUP : WM_LBUTTONUP,
356 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
358 if (flags & MOUSEEVENTF_RIGHTDOWN)
360 key_state_table[VK_RBUTTON] |= 0xc0;
361 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
362 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
364 if (flags & MOUSEEVENTF_RIGHTUP)
366 key_state_table[VK_RBUTTON] &= ~0x80;
367 queue_raw_mouse_message( GetSystemMetrics(SM_SWAPBUTTON) ? WM_LBUTTONUP : WM_RBUTTONUP,
368 hwnd, pt.x, pt.y, data, time, extra_info, injected_flags );
370 if (flags & MOUSEEVENTF_MIDDLEDOWN)
372 key_state_table[VK_MBUTTON] |= 0xc0;
373 queue_raw_mouse_message( WM_MBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
374 extra_info, injected_flags );
376 if (flags & MOUSEEVENTF_MIDDLEUP)
378 key_state_table[VK_MBUTTON] &= ~0x80;
379 queue_raw_mouse_message( WM_MBUTTONUP, hwnd, pt.x, pt.y, data, time,
380 extra_info, injected_flags );
382 if (flags & MOUSEEVENTF_WHEEL)
384 queue_raw_mouse_message( WM_MOUSEWHEEL, hwnd, pt.x, pt.y, data, time,
385 extra_info, injected_flags );
387 if (flags & MOUSEEVENTF_XDOWN)
389 key_state_table[VK_XBUTTON1 + data - 1] |= 0xc0;
390 queue_raw_mouse_message( WM_XBUTTONDOWN, hwnd, pt.x, pt.y, data, time,
391 extra_info, injected_flags );
393 if (flags & MOUSEEVENTF_XUP)
395 key_state_table[VK_XBUTTON1 + data - 1] &= ~0x80;
396 queue_raw_mouse_message( WM_XBUTTONUP, hwnd, pt.x, pt.y, data, time,
397 extra_info, injected_flags );
402 #ifdef SONAME_LIBXCURSOR
404 /***********************************************************************
405 * create_cursor_image
407 * Create an XcursorImage from a CURSORICONINFO
409 static XcursorImage *create_cursor_image( CURSORICONINFO *ptr )
411 static const unsigned char convert_5to8[] =
413 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
414 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
415 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
416 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
418 static const unsigned char convert_6to8[] =
420 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
421 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
422 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
423 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
424 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
425 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
426 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
427 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
429 int x;
430 int y;
431 int and_size;
432 unsigned char *and_bits, *and_ptr, *xor_bits, *xor_ptr;
433 int and_width_bytes, xor_width_bytes;
434 XcursorPixel *pixel_ptr;
435 XcursorImage *image;
436 unsigned char tmp;
437 BOOL alpha_zero = TRUE;
439 and_width_bytes = ptr->nWidth / 8;
440 xor_width_bytes = and_width_bytes * ptr->bBitsPerPixel;
442 and_size = ptr->nWidth * ptr->nHeight / 8;
443 and_ptr = and_bits = (unsigned char *)(ptr + 1);
445 xor_ptr = xor_bits = and_ptr + and_size;
447 image = pXcursorImageCreate( ptr->nWidth, ptr->nHeight );
448 pixel_ptr = image->pixels;
450 /* Generally 32 bit bitmaps have an alpha channel which is used in favor
451 * of the AND mask. However, if all pixels have alpha = 0x00, the bitmap
452 * is treated like one without alpha and the masks are used. As soon as
453 * one pixel has alpha != 0x00, and the mask ignored as described in the
454 * docs.
456 * This is most likely for applications which create the bitmaps with
457 * CreateDIBitmap, which creates a device dependent bitmap, so the format
458 * that arrives when loading depends on the screen's bpp. Apps that were
459 * written at 8 / 16 bpp times do not know about the 32 bit alpha, so
460 * they would get a completely transparent cursor on 32 bit displays.
462 * Non-32 bit bitmaps always use the AND mask
464 if(ptr->bBitsPerPixel == 32)
466 for (y = 0; alpha_zero && y < ptr->nHeight; ++y)
468 xor_ptr = xor_bits + (y * xor_width_bytes);
469 for (x = 0; x < ptr->nWidth; ++x)
471 if (xor_ptr[3] != 0x00)
473 alpha_zero = FALSE;
474 break;
476 xor_ptr+=4;
481 /* On windows, to calculate the color for a pixel, first an AND is done
482 * with the background and the "and" bitmap, then an XOR with the "xor"
483 * bitmap. This means that when the data in the "and" bitmap is 0, the
484 * pixel will get the color as specified in the "xor" bitmap.
485 * However, if the data in the "and" bitmap is 1, the result will be the
486 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
487 * data is completely black (0x000000) the pixel will become transparent,
488 * in case it's white (0xffffff) the pixel will become the inverse of the
489 * background color.
491 * Since we can't support inverting colors, we map the grayscale value of
492 * the "xor" data to the alpha channel, and xor the color with either
493 * black or white.
495 for (y = 0; y < ptr->nHeight; ++y)
497 and_ptr = and_bits + (y * and_width_bytes);
498 xor_ptr = xor_bits + (y * xor_width_bytes);
500 for (x = 0; x < ptr->nWidth; ++x)
502 /* Xcursor pixel data is in ARGB format, with A in the high byte */
503 switch (ptr->bBitsPerPixel)
505 case 32:
506 /* BGRA, 8 bits each */
507 *pixel_ptr = *xor_ptr++;
508 *pixel_ptr |= *xor_ptr++ << 8;
509 *pixel_ptr |= *xor_ptr++ << 16;
510 *pixel_ptr |= *xor_ptr++ << 24;
511 break;
513 case 24:
514 /* BGR, 8 bits each */
515 *pixel_ptr = *xor_ptr++;
516 *pixel_ptr |= *xor_ptr++ << 8;
517 *pixel_ptr |= *xor_ptr++ << 16;
518 break;
520 case 16:
521 /* BGR, 5 red, 6 green, 5 blue */
522 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
523 *pixel_ptr = convert_5to8[*xor_ptr & 0x1f];
524 tmp = (*xor_ptr++ & 0xe0) >> 5;
525 tmp |= (*xor_ptr & 0x07) << 3;
526 *pixel_ptr |= convert_6to8[tmp] << 16;
527 *pixel_ptr |= convert_5to8[*xor_ptr & 0xf8] << 24;
528 break;
530 case 1:
531 if (*xor_ptr & (1 << (7 - (x & 7)))) *pixel_ptr = 0xffffff;
532 else *pixel_ptr = 0;
533 if ((x & 7) == 7) ++xor_ptr;
534 break;
536 default:
537 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr->bBitsPerPixel);
538 return 0;
541 if (alpha_zero)
543 /* Alpha channel */
544 if (~*and_ptr & (1 << (7 - (x & 7)))) *pixel_ptr |= 0xff << 24;
545 else if (*pixel_ptr)
547 int alpha = (*pixel_ptr & 0xff) * 0.30f
548 + ((*pixel_ptr & 0xff00) >> 8) * 0.55f
549 + ((*pixel_ptr & 0xff0000) >> 16) * 0.15f;
550 *pixel_ptr ^= ((x + y) % 2) ? 0xffffff : 0x000000;
551 *pixel_ptr |= alpha << 24;
553 if ((x & 7) == 7) ++and_ptr;
555 ++pixel_ptr;
559 return image;
563 /***********************************************************************
564 * create_xcursor_cursor
566 * Use Xcursor to create an X cursor from a Windows one.
568 static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
570 Cursor cursor;
571 XcursorImage *image;
573 if (!ptr) /* Create an empty cursor */
575 image = pXcursorImageCreate( 1, 1 );
576 image->xhot = 0;
577 image->yhot = 0;
578 *(image->pixels) = 0;
579 cursor = pXcursorImageLoadCursor( display, image );
580 pXcursorImageDestroy( image );
582 return cursor;
585 image = create_cursor_image( ptr );
586 if (!image) return 0;
588 /* Make sure hotspot is valid */
589 image->xhot = ptr->ptHotSpot.x;
590 image->yhot = ptr->ptHotSpot.y;
591 if (image->xhot >= image->width ||
592 image->yhot >= image->height)
594 image->xhot = image->width / 2;
595 image->yhot = image->height / 2;
598 image->delay = 0;
600 cursor = pXcursorImageLoadCursor( display, image );
601 pXcursorImageDestroy( image );
603 return cursor;
606 #endif /* SONAME_LIBXCURSOR */
609 /***********************************************************************
610 * create_cursor
612 * Create an X cursor from a Windows one.
614 static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
616 Pixmap pixmapBits, pixmapMask, pixmapMaskInv = 0, pixmapAll;
617 XColor fg, bg;
618 Cursor cursor = None;
619 POINT hotspot;
620 char *bitMask32 = NULL;
622 #ifdef SONAME_LIBXCURSOR
623 if (pXcursorImageLoadCursor) return create_xcursor_cursor( display, ptr );
624 #endif
626 if (!ptr) /* Create an empty cursor */
628 static const char data[] = { 0 };
630 bg.red = bg.green = bg.blue = 0x0000;
631 pixmapBits = XCreateBitmapFromData( display, root_window, data, 1, 1 );
632 if (pixmapBits)
634 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
635 &bg, &bg, 0, 0 );
636 XFreePixmap( display, pixmapBits );
639 else /* Create the X cursor from the bits */
641 XImage *image;
642 GC gc;
644 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
645 ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
646 ptr->nWidthBytes);
648 /* Create a pixmap and transfer all the bits to it */
650 /* NOTE: Following hack works, but only because XFree depth
651 * 1 images really use 1 bit/pixel (and so the same layout
652 * as the Windows cursor data). Perhaps use a more generic
653 * algorithm here.
655 /* This pixmap will be written with two bitmaps. The first is
656 * the mask and the second is the image.
658 if (!(pixmapAll = XCreatePixmap( display, root_window,
659 ptr->nWidth, ptr->nHeight * 2, 1 )))
660 return 0;
661 if (!(image = XCreateImage( display, visual,
662 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
663 ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
665 XFreePixmap( display, pixmapAll );
666 return 0;
668 gc = XCreateGC( display, pixmapAll, 0, NULL );
669 XSetGraphicsExposures( display, gc, False );
670 image->byte_order = MSBFirst;
671 image->bitmap_bit_order = MSBFirst;
672 image->bitmap_unit = 16;
673 _XInitImageFuncPtrs(image);
674 if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
676 /* A plain old white on black cursor. */
677 fg.red = fg.green = fg.blue = 0xffff;
678 bg.red = bg.green = bg.blue = 0x0000;
679 XPutImage( display, pixmapAll, gc, image,
680 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
682 else
684 int rbits, gbits, bbits, red, green, blue;
685 int rfg, gfg, bfg, rbg, gbg, bbg;
686 int rscale, gscale, bscale;
687 int x, y, xmax, ymax, byteIndex, xorIndex;
688 unsigned char *theMask, *theImage, theChar;
689 int threshold, fgBits, bgBits, bitShifted;
690 BYTE pXorBits[128]; /* Up to 32x32 icons */
692 switch (ptr->bBitsPerPixel)
694 case 32:
695 bitMask32 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
696 ptr->nWidth * ptr->nHeight / 8 );
697 /* Fallthrough */
698 case 24:
699 rbits = 8;
700 gbits = 8;
701 bbits = 8;
702 threshold = 0x40;
703 break;
704 case 16:
705 rbits = 5;
706 gbits = 6;
707 bbits = 5;
708 threshold = 0x40;
709 break;
710 default:
711 FIXME("Currently no support for cursors with %d bits per pixel\n",
712 ptr->bBitsPerPixel);
713 XFreePixmap( display, pixmapAll );
714 XFreeGC( display, gc );
715 image->data = NULL;
716 XDestroyImage( image );
717 return 0;
719 /* The location of the mask. */
720 theMask = (unsigned char *)(ptr + 1);
721 /* The mask should still be 1 bit per pixel. The color image
722 * should immediately follow the mask.
724 theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
725 rfg = gfg = bfg = rbg = gbg = bbg = 0;
726 byteIndex = 0;
727 xorIndex = 0;
728 fgBits = 0;
729 bitShifted = 0x01;
730 xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
731 if (ptr->nWidth > 32) {
732 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
733 ptr->nWidth, ptr->nHeight);
735 ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
737 memset(pXorBits, 0, 128);
738 for (y=0; y<ymax; y++)
740 for (x=0; x<xmax; x++)
742 red = green = blue = 0;
743 switch (ptr->bBitsPerPixel)
745 case 32:
746 theChar = theImage[byteIndex++];
747 blue = theChar;
748 theChar = theImage[byteIndex++];
749 green = theChar;
750 theChar = theImage[byteIndex++];
751 red = theChar;
752 theChar = theImage[byteIndex++];
753 /* If the alpha channel is >5% transparent,
754 * assume that we can add it to the bitMask32.
756 if (theChar > 0x0D)
757 *(bitMask32 + (y*xmax+x)/8) |= 1 << (x & 7);
758 break;
759 case 24:
760 theChar = theImage[byteIndex++];
761 blue = theChar;
762 theChar = theImage[byteIndex++];
763 green = theChar;
764 theChar = theImage[byteIndex++];
765 red = theChar;
766 break;
767 case 16:
768 theChar = theImage[byteIndex++];
769 blue = theChar & 0x1F;
770 green = (theChar & 0xE0) >> 5;
771 theChar = theImage[byteIndex++];
772 green |= (theChar & 0x07) << 3;
773 red = (theChar & 0xF8) >> 3;
774 break;
777 if (red+green+blue > threshold)
779 rfg += red;
780 gfg += green;
781 bfg += blue;
782 fgBits++;
783 pXorBits[xorIndex] |= bitShifted;
785 else
787 rbg += red;
788 gbg += green;
789 bbg += blue;
791 if (x%8 == 7)
793 bitShifted = 0x01;
794 xorIndex++;
796 else
797 bitShifted = bitShifted << 1;
800 rscale = 1 << (16 - rbits);
801 gscale = 1 << (16 - gbits);
802 bscale = 1 << (16 - bbits);
803 if (fgBits)
805 fg.red = rfg * rscale / fgBits;
806 fg.green = gfg * gscale / fgBits;
807 fg.blue = bfg * bscale / fgBits;
809 else fg.red = fg.green = fg.blue = 0;
810 bgBits = xmax * ymax - fgBits;
811 if (bgBits)
813 bg.red = rbg * rscale / bgBits;
814 bg.green = gbg * gscale / bgBits;
815 bg.blue = bbg * bscale / bgBits;
817 else bg.red = bg.green = bg.blue = 0;
818 pixmapBits = XCreateBitmapFromData( display, root_window, (char *)pXorBits, xmax, ymax );
819 if (!pixmapBits)
821 HeapFree( GetProcessHeap(), 0, bitMask32 );
822 XFreePixmap( display, pixmapAll );
823 XFreeGC( display, gc );
824 image->data = NULL;
825 XDestroyImage( image );
826 return 0;
829 /* Put the mask. */
830 XPutImage( display, pixmapAll, gc, image,
831 0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
832 XSetFunction( display, gc, GXcopy );
833 /* Put the image */
834 XCopyArea( display, pixmapBits, pixmapAll, gc,
835 0, 0, xmax, ymax, 0, ptr->nHeight );
836 XFreePixmap( display, pixmapBits );
838 image->data = NULL;
839 XDestroyImage( image );
841 /* Now create the 2 pixmaps for bits and mask */
843 pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
844 if (ptr->bBitsPerPixel != 32)
846 pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
847 pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
849 /* Make sure everything went OK so far */
850 if (pixmapBits && pixmapMask && pixmapMaskInv)
852 /* We have to do some magic here, as cursors are not fully
853 * compatible between Windows and X11. Under X11, there are
854 * only 3 possible color cursor: black, white and masked. So
855 * we map the 4th Windows color (invert the bits on the screen)
856 * to black and an additional white bit on an other place
857 * (+1,+1). This require some boolean arithmetic:
859 * Windows | X11
860 * And Xor Result | Bits Mask Result
861 * 0 0 black | 0 1 background
862 * 0 1 white | 1 1 foreground
863 * 1 0 no change | X 0 no change
864 * 1 1 inverted | 0 1 background
866 * which gives:
867 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
868 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
870 * FIXME: apparently some servers do support 'inverted' color.
871 * I don't know if it's correct per the X spec, but maybe we
872 * ought to take advantage of it. -- AJ
874 XSetFunction( display, gc, GXcopy );
875 XCopyArea( display, pixmapAll, pixmapBits, gc,
876 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
877 XCopyArea( display, pixmapAll, pixmapMask, gc,
878 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
879 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
880 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
881 XSetFunction( display, gc, GXand );
882 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
883 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
884 XSetFunction( display, gc, GXandReverse );
885 XCopyArea( display, pixmapAll, pixmapBits, gc,
886 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
887 XSetFunction( display, gc, GXorReverse );
888 XCopyArea( display, pixmapAll, pixmapMask, gc,
889 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
890 /* Additional white */
891 XSetFunction( display, gc, GXor );
892 XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
893 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
894 XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
895 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
896 XSetFunction( display, gc, GXcopy );
899 else
901 pixmapMask = XCreateBitmapFromData( display, root_window,
902 bitMask32, ptr->nWidth,
903 ptr->nHeight );
904 HeapFree( GetProcessHeap(), 0, bitMask32 );
907 /* Make sure hotspot is valid */
908 hotspot.x = ptr->ptHotSpot.x;
909 hotspot.y = ptr->ptHotSpot.y;
910 if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
911 hotspot.y < 0 || hotspot.y >= ptr->nHeight)
913 hotspot.x = ptr->nWidth / 2;
914 hotspot.y = ptr->nHeight / 2;
917 if (pixmapBits && pixmapMask)
918 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
919 &fg, &bg, hotspot.x, hotspot.y );
921 /* Now free everything */
923 if (pixmapAll) XFreePixmap( display, pixmapAll );
924 if (pixmapBits) XFreePixmap( display, pixmapBits );
925 if (pixmapMask) XFreePixmap( display, pixmapMask );
926 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
927 XFreeGC( display, gc );
929 return cursor;
933 /***********************************************************************
934 * SetCursor (X11DRV.@)
936 void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
938 struct x11drv_thread_data *data = x11drv_init_thread_data();
939 Cursor cursor;
941 if (lpCursor)
942 TRACE("%ux%u, planes %u, bpp %u\n",
943 lpCursor->nWidth, lpCursor->nHeight, lpCursor->bPlanes, lpCursor->bBitsPerPixel);
944 else
945 TRACE("NULL\n");
947 /* set the same cursor for all top-level windows of the current thread */
949 wine_tsx11_lock();
950 cursor = create_cursor( data->display, lpCursor );
951 if (cursor)
953 if (data->cursor) XFreeCursor( data->display, data->cursor );
954 data->cursor = cursor;
955 if (data->cursor_window)
957 XDefineCursor( data->display, data->cursor_window, cursor );
958 /* Make the change take effect immediately */
959 XFlush( data->display );
962 wine_tsx11_unlock();
965 /***********************************************************************
966 * SetCursorPos (X11DRV.@)
968 BOOL X11DRV_SetCursorPos( INT x, INT y )
970 Display *display = thread_init_display();
971 POINT pt;
973 TRACE( "warping to (%d,%d)\n", x, y );
975 wine_tsx11_lock();
976 if (cursor_pos.x == x && cursor_pos.y == y)
978 wine_tsx11_unlock();
979 /* We still need to generate WM_MOUSEMOVE */
980 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
981 return TRUE;
984 pt.x = x; pt.y = y;
985 clip_point_to_rect( &cursor_clip, &pt);
986 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
987 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
988 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
989 cursor_pos = pt;
990 wine_tsx11_unlock();
991 return TRUE;
994 /***********************************************************************
995 * GetCursorPos (X11DRV.@)
997 BOOL X11DRV_GetCursorPos(LPPOINT pos)
999 Display *display = thread_init_display();
1000 Window root, child;
1001 int rootX, rootY, winX, winY;
1002 unsigned int xstate;
1004 wine_tsx11_lock();
1005 if ((GetTickCount() - last_time_modified > 100) &&
1006 XQueryPointer( display, root_window, &root, &child,
1007 &rootX, &rootY, &winX, &winY, &xstate ))
1009 update_button_state( xstate );
1010 winX += virtual_screen_rect.left;
1011 winY += virtual_screen_rect.top;
1012 TRACE("pointer at (%d,%d)\n", winX, winY );
1013 cursor_pos.x = winX;
1014 cursor_pos.y = winY;
1016 *pos = cursor_pos;
1017 wine_tsx11_unlock();
1018 return TRUE;
1022 /***********************************************************************
1023 * ClipCursor (X11DRV.@)
1025 * Set the cursor clipping rectangle.
1027 BOOL X11DRV_ClipCursor( LPCRECT clip )
1029 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
1030 cursor_clip = virtual_screen_rect;
1032 return TRUE;
1035 /***********************************************************************
1036 * X11DRV_ButtonPress
1038 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
1040 XButtonEvent *event = &xev->xbutton;
1041 int buttonNum = event->button - 1;
1042 WORD wData = 0;
1043 POINT pt;
1045 if (buttonNum >= NB_BUTTONS) return;
1046 if (!hwnd) return;
1048 switch (buttonNum)
1050 case 3:
1051 wData = WHEEL_DELTA;
1052 break;
1053 case 4:
1054 wData = -WHEEL_DELTA;
1055 break;
1056 case 5:
1057 wData = XBUTTON1;
1058 break;
1059 case 6:
1060 wData = XBUTTON2;
1061 break;
1062 case 7:
1063 wData = XBUTTON1;
1064 break;
1065 case 8:
1066 wData = XBUTTON2;
1067 break;
1070 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1072 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1073 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1077 /***********************************************************************
1078 * X11DRV_ButtonRelease
1080 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
1082 XButtonEvent *event = &xev->xbutton;
1083 int buttonNum = event->button - 1;
1084 WORD wData = 0;
1085 POINT pt;
1087 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
1088 if (!hwnd) return;
1090 switch (buttonNum)
1092 case 5:
1093 wData = XBUTTON1;
1094 break;
1095 case 6:
1096 wData = XBUTTON2;
1097 break;
1098 case 7:
1099 wData = XBUTTON1;
1100 break;
1101 case 8:
1102 wData = XBUTTON2;
1103 break;
1106 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1108 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1109 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1113 /***********************************************************************
1114 * X11DRV_MotionNotify
1116 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
1118 XMotionEvent *event = &xev->xmotion;
1119 POINT pt;
1121 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
1123 if (!hwnd) return;
1125 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1127 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1128 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1132 /***********************************************************************
1133 * X11DRV_EnterNotify
1135 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
1137 XCrossingEvent *event = &xev->xcrossing;
1138 POINT pt;
1140 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
1142 if (!hwnd) return;
1143 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
1144 if (event->window == x11drv_thread_data()->grab_window) return;
1146 /* simulate a mouse motion event */
1147 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1149 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1150 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );