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