credui: Convert the German resources to UTF-8.
[wine/hacks.git] / dlls / winex11.drv / mouse.c
blob52824125a887fac42bc3b26c2320c4c5e4b01034
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 CDECL 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 /***********************************************************************
403 * check_alpha_zero
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)
420 int x, y;
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)
432 return FALSE;
434 xor_ptr+=4;
439 return TRUE;
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,
470 int x;
471 int y;
472 int and_size;
473 unsigned char *and_bits, *and_ptr, *xor_bits, *xor_ptr;
474 int and_width_bytes, xor_width_bytes;
475 XcursorPixel *pixel_ptr;
476 XcursorImage *image;
477 unsigned char tmp;
478 BOOL alpha_zero;
480 and_width_bytes = 2 * ((ptr->nWidth+15) / 16);
481 xor_width_bytes = ptr->nWidthBytes;
483 and_size = ptr->nHeight * and_width_bytes;
484 and_ptr = and_bits = (unsigned char *)(ptr + 1);
486 xor_ptr = xor_bits = and_ptr + and_size;
488 image = pXcursorImageCreate( ptr->nWidth, ptr->nHeight );
489 pixel_ptr = image->pixels;
491 alpha_zero = check_alpha_zero(ptr, xor_bits);
493 /* On windows, to calculate the color for a pixel, first an AND is done
494 * with the background and the "and" bitmap, then an XOR with the "xor"
495 * bitmap. This means that when the data in the "and" bitmap is 0, the
496 * pixel will get the color as specified in the "xor" bitmap.
497 * However, if the data in the "and" bitmap is 1, the result will be the
498 * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
499 * data is completely black (0x000000) the pixel will become transparent,
500 * in case it's white (0xffffff) the pixel will become the inverse of the
501 * background color.
503 * Since we can't support inverting colors, we map the grayscale value of
504 * the "xor" data to the alpha channel, and xor the color with either
505 * black or white.
507 for (y = 0; y < ptr->nHeight; ++y)
509 and_ptr = and_bits + (y * and_width_bytes);
510 xor_ptr = xor_bits + (y * xor_width_bytes);
512 for (x = 0; x < ptr->nWidth; ++x)
514 /* Xcursor pixel data is in ARGB format, with A in the high byte */
515 switch (ptr->bBitsPerPixel)
517 case 32:
518 /* BGRA, 8 bits each */
519 *pixel_ptr = *xor_ptr++;
520 *pixel_ptr |= *xor_ptr++ << 8;
521 *pixel_ptr |= *xor_ptr++ << 16;
522 *pixel_ptr |= *xor_ptr++ << 24;
523 break;
525 case 24:
526 /* BGR, 8 bits each */
527 *pixel_ptr = *xor_ptr++;
528 *pixel_ptr |= *xor_ptr++ << 8;
529 *pixel_ptr |= *xor_ptr++ << 16;
530 break;
532 case 16:
533 /* BGR, 5 red, 6 green, 5 blue */
534 /* [gggbbbbb][rrrrrggg] -> [xxxxxxxx][rrrrrrrr][gggggggg][bbbbbbbb] */
535 *pixel_ptr = convert_5to8[*xor_ptr & 0x1f];
536 tmp = (*xor_ptr++ & 0xe0) >> 5;
537 tmp |= (*xor_ptr & 0x07) << 3;
538 *pixel_ptr |= convert_6to8[tmp] << 16;
539 *pixel_ptr |= convert_5to8[*xor_ptr & 0xf8] << 24;
540 break;
542 case 1:
543 if (*xor_ptr & (1 << (7 - (x & 7)))) *pixel_ptr = 0xffffff;
544 else *pixel_ptr = 0;
545 if ((x & 7) == 7) ++xor_ptr;
546 break;
548 default:
549 FIXME("Currently no support for cursors with %d bits per pixel\n", ptr->bBitsPerPixel);
550 return 0;
553 if (alpha_zero)
555 /* Alpha channel */
556 if (~*and_ptr & (1 << (7 - (x & 7)))) *pixel_ptr |= 0xff << 24;
557 else if (*pixel_ptr)
559 int alpha = (*pixel_ptr & 0xff) * 0.30f
560 + ((*pixel_ptr & 0xff00) >> 8) * 0.55f
561 + ((*pixel_ptr & 0xff0000) >> 16) * 0.15f;
562 *pixel_ptr ^= ((x + y) % 2) ? 0xffffff : 0x000000;
563 *pixel_ptr |= alpha << 24;
565 if ((x & 7) == 7) ++and_ptr;
567 ++pixel_ptr;
571 return image;
575 /***********************************************************************
576 * create_xcursor_cursor
578 * Use Xcursor to create an X cursor from a Windows one.
580 static Cursor create_xcursor_cursor( Display *display, CURSORICONINFO *ptr )
582 Cursor cursor;
583 XcursorImage *image;
585 if (!ptr) /* Create an empty cursor */
587 image = pXcursorImageCreate( 1, 1 );
588 image->xhot = 0;
589 image->yhot = 0;
590 *(image->pixels) = 0;
591 cursor = pXcursorImageLoadCursor( display, image );
592 pXcursorImageDestroy( image );
594 return cursor;
597 image = create_cursor_image( ptr );
598 if (!image) return 0;
600 /* Make sure hotspot is valid */
601 image->xhot = ptr->ptHotSpot.x;
602 image->yhot = ptr->ptHotSpot.y;
603 if (image->xhot >= image->width ||
604 image->yhot >= image->height)
606 image->xhot = image->width / 2;
607 image->yhot = image->height / 2;
610 image->delay = 0;
612 cursor = pXcursorImageLoadCursor( display, image );
613 pXcursorImageDestroy( image );
615 return cursor;
618 #endif /* SONAME_LIBXCURSOR */
621 /***********************************************************************
622 * create_cursor
624 * Create an X cursor from a Windows one.
626 static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
628 Pixmap pixmapBits, pixmapMask, pixmapMaskInv = 0, pixmapAll;
629 XColor fg, bg;
630 Cursor cursor = None;
631 POINT hotspot;
632 char *bitMask32 = NULL;
633 BOOL alpha_zero = TRUE;
635 #ifdef SONAME_LIBXCURSOR
636 if (pXcursorImageLoadCursor) return create_xcursor_cursor( display, ptr );
637 #endif
639 if (!ptr) /* Create an empty cursor */
641 static const char data[] = { 0 };
643 bg.red = bg.green = bg.blue = 0x0000;
644 pixmapBits = XCreateBitmapFromData( display, root_window, data, 1, 1 );
645 if (pixmapBits)
647 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
648 &bg, &bg, 0, 0 );
649 XFreePixmap( display, pixmapBits );
652 else /* Create the X cursor from the bits */
654 XImage *image;
655 GC gc;
657 TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
658 ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
659 ptr->nWidthBytes);
661 /* Create a pixmap and transfer all the bits to it */
663 /* NOTE: Following hack works, but only because XFree depth
664 * 1 images really use 1 bit/pixel (and so the same layout
665 * as the Windows cursor data). Perhaps use a more generic
666 * algorithm here.
668 /* This pixmap will be written with two bitmaps. The first is
669 * the mask and the second is the image.
671 if (!(pixmapAll = XCreatePixmap( display, root_window,
672 ptr->nWidth, ptr->nHeight * 2, 1 )))
673 return 0;
674 if (!(image = XCreateImage( display, visual,
675 1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
676 ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
678 XFreePixmap( display, pixmapAll );
679 return 0;
681 gc = XCreateGC( display, pixmapAll, 0, NULL );
682 XSetGraphicsExposures( display, gc, False );
683 image->byte_order = MSBFirst;
684 image->bitmap_bit_order = MSBFirst;
685 image->bitmap_unit = 16;
686 _XInitImageFuncPtrs(image);
687 if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
689 /* A plain old white on black cursor. */
690 fg.red = fg.green = fg.blue = 0xffff;
691 bg.red = bg.green = bg.blue = 0x0000;
692 XPutImage( display, pixmapAll, gc, image,
693 0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
695 else
697 int rbits, gbits, bbits, red, green, blue;
698 int rfg, gfg, bfg, rbg, gbg, bbg;
699 int rscale, gscale, bscale;
700 int x, y, xmax, ymax, byteIndex, xorIndex;
701 unsigned char *theMask, *theImage, theChar;
702 int threshold, fgBits, bgBits, bitShifted;
703 BYTE pXorBits[128]; /* Up to 32x32 icons */
705 switch (ptr->bBitsPerPixel)
707 case 32:
708 bitMask32 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
709 ptr->nWidth * ptr->nHeight / 8 );
710 /* Fallthrough */
711 case 24:
712 rbits = 8;
713 gbits = 8;
714 bbits = 8;
715 threshold = 0x40;
716 break;
717 case 16:
718 rbits = 5;
719 gbits = 6;
720 bbits = 5;
721 threshold = 0x40;
722 break;
723 default:
724 FIXME("Currently no support for cursors with %d bits per pixel\n",
725 ptr->bBitsPerPixel);
726 XFreePixmap( display, pixmapAll );
727 XFreeGC( display, gc );
728 image->data = NULL;
729 XDestroyImage( image );
730 return 0;
732 /* The location of the mask. */
733 theMask = (unsigned char *)(ptr + 1);
734 /* The mask should still be 1 bit per pixel. The color image
735 * should immediately follow the mask.
737 theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
738 rfg = gfg = bfg = rbg = gbg = bbg = 0;
739 byteIndex = 0;
740 xorIndex = 0;
741 fgBits = 0;
742 bitShifted = 0x01;
743 xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
744 if (ptr->nWidth > 32) {
745 ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
746 ptr->nWidth, ptr->nHeight);
748 ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
749 alpha_zero = check_alpha_zero(ptr, theImage);
751 memset(pXorBits, 0, 128);
752 for (y=0; y<ymax; y++)
754 for (x=0; x<xmax; x++)
756 red = green = blue = 0;
757 switch (ptr->bBitsPerPixel)
759 case 32:
760 theChar = theImage[byteIndex++];
761 blue = theChar;
762 theChar = theImage[byteIndex++];
763 green = theChar;
764 theChar = theImage[byteIndex++];
765 red = theChar;
766 theChar = theImage[byteIndex++];
767 /* If the alpha channel is >5% transparent,
768 * assume that we can add it to the bitMask32.
770 if (theChar > 0x0D)
771 *(bitMask32 + (y*xmax+x)/8) |= 1 << (x & 7);
772 break;
773 case 24:
774 theChar = theImage[byteIndex++];
775 blue = theChar;
776 theChar = theImage[byteIndex++];
777 green = theChar;
778 theChar = theImage[byteIndex++];
779 red = theChar;
780 break;
781 case 16:
782 theChar = theImage[byteIndex++];
783 blue = theChar & 0x1F;
784 green = (theChar & 0xE0) >> 5;
785 theChar = theImage[byteIndex++];
786 green |= (theChar & 0x07) << 3;
787 red = (theChar & 0xF8) >> 3;
788 break;
791 if (red+green+blue > threshold)
793 rfg += red;
794 gfg += green;
795 bfg += blue;
796 fgBits++;
797 pXorBits[xorIndex] |= bitShifted;
799 else
801 rbg += red;
802 gbg += green;
803 bbg += blue;
805 if (x%8 == 7)
807 bitShifted = 0x01;
808 xorIndex++;
810 else
811 bitShifted = bitShifted << 1;
814 rscale = 1 << (16 - rbits);
815 gscale = 1 << (16 - gbits);
816 bscale = 1 << (16 - bbits);
817 if (fgBits)
819 fg.red = rfg * rscale / fgBits;
820 fg.green = gfg * gscale / fgBits;
821 fg.blue = bfg * bscale / fgBits;
823 else fg.red = fg.green = fg.blue = 0;
824 bgBits = xmax * ymax - fgBits;
825 if (bgBits)
827 bg.red = rbg * rscale / bgBits;
828 bg.green = gbg * gscale / bgBits;
829 bg.blue = bbg * bscale / bgBits;
831 else bg.red = bg.green = bg.blue = 0;
832 pixmapBits = XCreateBitmapFromData( display, root_window, (char *)pXorBits, xmax, ymax );
833 if (!pixmapBits)
835 HeapFree( GetProcessHeap(), 0, bitMask32 );
836 XFreePixmap( display, pixmapAll );
837 XFreeGC( display, gc );
838 image->data = NULL;
839 XDestroyImage( image );
840 return 0;
843 /* Put the mask. */
844 XPutImage( display, pixmapAll, gc, image,
845 0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
846 XSetFunction( display, gc, GXcopy );
847 /* Put the image */
848 XCopyArea( display, pixmapBits, pixmapAll, gc,
849 0, 0, xmax, ymax, 0, ptr->nHeight );
850 XFreePixmap( display, pixmapBits );
852 image->data = NULL;
853 XDestroyImage( image );
855 /* Now create the 2 pixmaps for bits and mask */
857 pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
858 if (alpha_zero)
860 pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
861 pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
863 /* Make sure everything went OK so far */
864 if (pixmapBits && pixmapMask && pixmapMaskInv)
866 /* We have to do some magic here, as cursors are not fully
867 * compatible between Windows and X11. Under X11, there are
868 * only 3 possible color cursor: black, white and masked. So
869 * we map the 4th Windows color (invert the bits on the screen)
870 * to black and an additional white bit on an other place
871 * (+1,+1). This require some boolean arithmetic:
873 * Windows | X11
874 * And Xor Result | Bits Mask Result
875 * 0 0 black | 0 1 background
876 * 0 1 white | 1 1 foreground
877 * 1 0 no change | X 0 no change
878 * 1 1 inverted | 0 1 background
880 * which gives:
881 * Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
882 * Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
884 * FIXME: apparently some servers do support 'inverted' color.
885 * I don't know if it's correct per the X spec, but maybe we
886 * ought to take advantage of it. -- AJ
888 XSetFunction( display, gc, GXcopy );
889 XCopyArea( display, pixmapAll, pixmapBits, gc,
890 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
891 XCopyArea( display, pixmapAll, pixmapMask, gc,
892 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
893 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
894 0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
895 XSetFunction( display, gc, GXand );
896 XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
897 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
898 XSetFunction( display, gc, GXandReverse );
899 XCopyArea( display, pixmapAll, pixmapBits, gc,
900 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
901 XSetFunction( display, gc, GXorReverse );
902 XCopyArea( display, pixmapAll, pixmapMask, gc,
903 0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
904 /* Additional white */
905 XSetFunction( display, gc, GXor );
906 XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
907 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
908 XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
909 0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
910 XSetFunction( display, gc, GXcopy );
913 else
915 pixmapMask = XCreateBitmapFromData( display, root_window,
916 bitMask32, ptr->nWidth,
917 ptr->nHeight );
920 /* Make sure hotspot is valid */
921 hotspot.x = ptr->ptHotSpot.x;
922 hotspot.y = ptr->ptHotSpot.y;
923 if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
924 hotspot.y < 0 || hotspot.y >= ptr->nHeight)
926 hotspot.x = ptr->nWidth / 2;
927 hotspot.y = ptr->nHeight / 2;
930 if (pixmapBits && pixmapMask)
931 cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
932 &fg, &bg, hotspot.x, hotspot.y );
934 /* Now free everything */
936 if (pixmapAll) XFreePixmap( display, pixmapAll );
937 if (pixmapBits) XFreePixmap( display, pixmapBits );
938 if (pixmapMask) XFreePixmap( display, pixmapMask );
939 if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
940 HeapFree( GetProcessHeap(), 0, bitMask32 );
941 XFreeGC( display, gc );
943 return cursor;
947 /***********************************************************************
948 * SetCursor (X11DRV.@)
950 void CDECL X11DRV_SetCursor( CURSORICONINFO *lpCursor )
952 struct x11drv_thread_data *data = x11drv_init_thread_data();
953 Cursor cursor;
955 if (lpCursor)
956 TRACE("%ux%u, planes %u, bpp %u\n",
957 lpCursor->nWidth, lpCursor->nHeight, lpCursor->bPlanes, lpCursor->bBitsPerPixel);
958 else
959 TRACE("NULL\n");
961 /* set the same cursor for all top-level windows of the current thread */
963 wine_tsx11_lock();
964 cursor = create_cursor( data->display, lpCursor );
965 if (cursor)
967 if (data->cursor) XFreeCursor( data->display, data->cursor );
968 data->cursor = cursor;
969 if (data->cursor_window)
971 XDefineCursor( data->display, data->cursor_window, cursor );
972 /* Make the change take effect immediately */
973 XFlush( data->display );
976 wine_tsx11_unlock();
979 /***********************************************************************
980 * SetCursorPos (X11DRV.@)
982 BOOL CDECL X11DRV_SetCursorPos( INT x, INT y )
984 Display *display = thread_init_display();
985 POINT pt;
987 TRACE( "warping to (%d,%d)\n", x, y );
989 wine_tsx11_lock();
990 if (cursor_pos.x == x && cursor_pos.y == y)
992 wine_tsx11_unlock();
993 /* We still need to generate WM_MOUSEMOVE */
994 queue_raw_mouse_message( WM_MOUSEMOVE, NULL, x, y, 0, GetCurrentTime(), 0, 0 );
995 return TRUE;
998 pt.x = x; pt.y = y;
999 clip_point_to_rect( &cursor_clip, &pt);
1000 XWarpPointer( display, root_window, root_window, 0, 0, 0, 0,
1001 pt.x - virtual_screen_rect.left, pt.y - virtual_screen_rect.top );
1002 XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */
1003 cursor_pos = pt;
1004 wine_tsx11_unlock();
1005 return TRUE;
1008 /***********************************************************************
1009 * GetCursorPos (X11DRV.@)
1011 BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos)
1013 Display *display = thread_init_display();
1014 Window root, child;
1015 int rootX, rootY, winX, winY;
1016 unsigned int xstate;
1018 wine_tsx11_lock();
1019 if ((GetTickCount() - last_time_modified > 100) &&
1020 XQueryPointer( display, root_window, &root, &child,
1021 &rootX, &rootY, &winX, &winY, &xstate ))
1023 update_button_state( xstate );
1024 winX += virtual_screen_rect.left;
1025 winY += virtual_screen_rect.top;
1026 TRACE("pointer at (%d,%d)\n", winX, winY );
1027 cursor_pos.x = winX;
1028 cursor_pos.y = winY;
1030 *pos = cursor_pos;
1031 wine_tsx11_unlock();
1032 return TRUE;
1036 /***********************************************************************
1037 * ClipCursor (X11DRV.@)
1039 * Set the cursor clipping rectangle.
1041 BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
1043 if (!IntersectRect( &cursor_clip, &virtual_screen_rect, clip ))
1044 cursor_clip = virtual_screen_rect;
1046 return TRUE;
1049 /***********************************************************************
1050 * X11DRV_ButtonPress
1052 void X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
1054 XButtonEvent *event = &xev->xbutton;
1055 int buttonNum = event->button - 1;
1056 WORD wData = 0;
1057 POINT pt;
1059 if (buttonNum >= NB_BUTTONS) return;
1060 if (!hwnd) return;
1062 switch (buttonNum)
1064 case 3:
1065 wData = WHEEL_DELTA;
1066 break;
1067 case 4:
1068 wData = -WHEEL_DELTA;
1069 break;
1070 case 5:
1071 wData = XBUTTON1;
1072 break;
1073 case 6:
1074 wData = XBUTTON2;
1075 break;
1076 case 7:
1077 wData = XBUTTON1;
1078 break;
1079 case 8:
1080 wData = XBUTTON2;
1081 break;
1084 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1086 X11DRV_send_mouse_input( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1087 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1091 /***********************************************************************
1092 * X11DRV_ButtonRelease
1094 void X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
1096 XButtonEvent *event = &xev->xbutton;
1097 int buttonNum = event->button - 1;
1098 WORD wData = 0;
1099 POINT pt;
1101 if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
1102 if (!hwnd) return;
1104 switch (buttonNum)
1106 case 5:
1107 wData = XBUTTON1;
1108 break;
1109 case 6:
1110 wData = XBUTTON2;
1111 break;
1112 case 7:
1113 wData = XBUTTON1;
1114 break;
1115 case 8:
1116 wData = XBUTTON2;
1117 break;
1120 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1122 X11DRV_send_mouse_input( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1123 pt.x, pt.y, wData, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1127 /***********************************************************************
1128 * X11DRV_MotionNotify
1130 void X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
1132 XMotionEvent *event = &xev->xmotion;
1133 POINT pt;
1135 TRACE("hwnd %p, event->is_hint %d\n", hwnd, event->is_hint);
1137 if (!hwnd) return;
1139 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1141 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1142 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );
1146 /***********************************************************************
1147 * X11DRV_EnterNotify
1149 void X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
1151 XCrossingEvent *event = &xev->xcrossing;
1152 POINT pt;
1154 TRACE("hwnd %p, event->detail %d\n", hwnd, event->detail);
1156 if (!hwnd) return;
1157 if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
1158 if (event->window == x11drv_thread_data()->grab_window) return;
1160 /* simulate a mouse motion event */
1161 update_mouse_state( hwnd, event->window, event->x, event->y, event->state, &pt );
1163 X11DRV_send_mouse_input( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
1164 pt.x, pt.y, 0, EVENT_x11_time_to_win32_time(event->time), 0, 0 );