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