wineps: Copy GetTextExtentExPoint implementation to unixlib.
[wine.git] / dlls / winemac.drv / mouse.c
blobcb194095d558f36c0ef01fc6f5bd3e6140a8d28f
1 /*
2 * MACDRV mouse driver
4 * Copyright 1998 Ulrich Weigand
5 * Copyright 2007 Henri Verbeet
6 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #if 0
24 #pragma makedep unix
25 #endif
27 #include "config.h"
29 #define OEMRESOURCE
30 #include "macdrv.h"
31 #include "wine/server.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
36 static pthread_mutex_t cursor_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
37 static CFMutableDictionaryRef cursor_cache;
40 struct system_cursors
42 WORD id;
43 CFStringRef name;
46 static const struct system_cursors user32_cursors[] =
48 { OCR_NORMAL, CFSTR("arrowCursor") },
49 { OCR_IBEAM, CFSTR("IBeamCursor") },
50 { OCR_CROSS, CFSTR("crosshairCursor") },
51 { OCR_SIZEWE, CFSTR("resizeLeftRightCursor") },
52 { OCR_SIZENS, CFSTR("resizeUpDownCursor") },
53 { OCR_NO, CFSTR("operationNotAllowedCursor") },
54 { OCR_HAND, CFSTR("pointingHandCursor") },
55 { 0 }
58 static const struct system_cursors comctl32_cursors[] =
60 { 102, CFSTR("closedHandCursor") },
61 { 104, CFSTR("dragCopyCursor") },
62 { 105, CFSTR("arrowCursor") },
63 { 106, CFSTR("resizeLeftRightCursor") },
64 { 107, CFSTR("resizeLeftRightCursor") },
65 { 108, CFSTR("pointingHandCursor") },
66 { 135, CFSTR("resizeUpDownCursor") },
67 { 0 }
70 static const struct system_cursors ole32_cursors[] =
72 { 1, CFSTR("operationNotAllowedCursor") },
73 { 2, CFSTR("closedHandCursor") },
74 { 3, CFSTR("dragCopyCursor") },
75 { 4, CFSTR("dragLinkCursor") },
76 { 0 }
79 static const struct system_cursors riched20_cursors[] =
81 { 105, CFSTR("pointingHandCursor") },
82 { 109, CFSTR("dragCopyCursor") },
83 { 110, CFSTR("closedHandCursor") },
84 { 111, CFSTR("operationNotAllowedCursor") },
85 { 0 }
88 static const struct
90 const struct system_cursors *cursors;
91 WCHAR name[16];
92 } module_cursors[] =
94 { user32_cursors, {'u','s','e','r','3','2','.','d','l','l',0} },
95 { comctl32_cursors, {'c','o','m','c','t','l','3','2','.','d','l','l',0} },
96 { ole32_cursors, {'o','l','e','3','2','.','d','l','l',0} },
97 { riched20_cursors, {'r','i','c','h','e','d','2','0','.','d','l','l',0} }
100 /* The names of NSCursor class methods which return cursor objects. */
101 static const CFStringRef cocoa_cursor_names[] =
103 CFSTR("arrowCursor"),
104 CFSTR("closedHandCursor"),
105 CFSTR("contextualMenuCursor"),
106 CFSTR("crosshairCursor"),
107 CFSTR("disappearingItemCursor"),
108 CFSTR("dragCopyCursor"),
109 CFSTR("dragLinkCursor"),
110 CFSTR("IBeamCursor"),
111 CFSTR("IBeamCursorForVerticalLayout"),
112 CFSTR("openHandCursor"),
113 CFSTR("operationNotAllowedCursor"),
114 CFSTR("pointingHandCursor"),
115 CFSTR("resizeDownCursor"),
116 CFSTR("resizeLeftCursor"),
117 CFSTR("resizeLeftRightCursor"),
118 CFSTR("resizeRightCursor"),
119 CFSTR("resizeUpCursor"),
120 CFSTR("resizeUpDownCursor"),
124 /***********************************************************************
125 * send_mouse_input
127 * Update the various window states on a mouse event.
129 static void send_mouse_input(HWND hwnd, macdrv_window cocoa_window, UINT flags, int x, int y,
130 DWORD mouse_data, BOOL drag, unsigned long time)
132 INPUT input;
133 HWND top_level_hwnd;
135 top_level_hwnd = NtUserGetAncestor(hwnd, GA_ROOT);
137 if ((flags & MOUSEEVENTF_MOVE) && (flags & MOUSEEVENTF_ABSOLUTE) && !drag &&
138 cocoa_window != macdrv_thread_data()->capture_window)
140 /* update the wine server Z-order */
141 SERVER_START_REQ(update_window_zorder)
143 req->window = wine_server_user_handle(top_level_hwnd);
144 req->rect.left = x;
145 req->rect.top = y;
146 req->rect.right = x + 1;
147 req->rect.bottom = y + 1;
148 wine_server_call(req);
150 SERVER_END_REQ;
153 input.type = INPUT_MOUSE;
154 input.mi.dx = x;
155 input.mi.dy = y;
156 input.mi.mouseData = mouse_data;
157 input.mi.dwFlags = flags;
158 input.mi.time = time;
159 input.mi.dwExtraInfo = 0;
161 __wine_send_input(top_level_hwnd, &input, NULL);
165 /***********************************************************************
166 * copy_system_cursor_name
168 CFStringRef copy_system_cursor_name(ICONINFOEXW *info)
170 const struct system_cursors *cursors;
171 unsigned int i;
172 CFStringRef cursor_name = NULL;
173 const WCHAR *module;
174 HKEY key;
175 WCHAR *p, name[MAX_PATH * 2];
177 TRACE("info->szModName %s info->szResName %s info->wResID %hu\n", debugstr_w(info->szModName),
178 debugstr_w(info->szResName), info->wResID);
180 if (!info->szModName[0]) return NULL;
182 p = wcsrchr(info->szModName, '\\');
183 wcscpy(name, p ? p + 1 : info->szModName);
184 p = name + wcslen(name);
185 *p++ = ',';
186 if (info->szResName[0]) wcscpy(p, info->szResName);
187 else
189 char buf[16];
190 sprintf(buf, "%hu", info->wResID);
191 asciiz_to_unicode(p, buf);
194 /* @@ Wine registry key: HKCU\Software\Wine\Mac Driver\Cursors */
195 if (!(key = open_hkcu_key("Software\\Wine\\Mac Driver\\Cursors")))
197 char buffer[2048];
198 KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buffer;
199 DWORD ret;
201 ret = query_reg_value(key, name, info, sizeof(buffer));
202 NtClose(key);
203 if (ret)
205 const WCHAR *value = (const WCHAR *)info->Data;
206 if (!value[0])
208 TRACE("registry forces standard cursor for %s\n", debugstr_w(name));
209 return NULL; /* force standard cursor */
212 cursor_name = CFStringCreateWithCharacters(NULL, value, wcslen(value));
213 if (!cursor_name)
215 WARN("CFStringCreateWithCharacters failed for %s\n", debugstr_w(value));
216 return NULL;
219 /* Make sure it's one of the appropriate NSCursor class methods. */
220 for (i = 0; i < ARRAY_SIZE(cocoa_cursor_names); i++)
221 if (CFEqual(cursor_name, cocoa_cursor_names[i]))
222 goto done;
224 WARN("%s mapped to invalid Cocoa cursor name %s\n", debugstr_w(name), debugstr_w(value));
225 CFRelease(cursor_name);
226 return NULL;
230 if (info->szResName[0]) goto done; /* only integer resources are supported here */
232 if ((module = wcsrchr(info->szModName, '\\'))) module++;
233 else module = info->szModName;
234 for (i = 0; i < ARRAY_SIZE( module_cursors ); i++)
235 if (!wcsicmp(module, module_cursors[i].name)) break;
236 if (i == ARRAY_SIZE(module_cursors)) goto done;
238 cursors = module_cursors[i].cursors;
239 for (i = 0; cursors[i].id; i++)
240 if (cursors[i].id == info->wResID)
242 cursor_name = CFRetain(cursors[i].name);
243 break;
246 done:
247 if (cursor_name)
248 TRACE("%s -> %s\n", debugstr_w(name), debugstr_cf(cursor_name));
249 else
250 WARN("no system cursor found for %s\n", debugstr_w(name));
251 return cursor_name;
254 /***********************************************************************
255 * create_monochrome_cursor
257 CFArrayRef create_monochrome_cursor(HDC hdc, const ICONINFOEXW *icon, int width, int height)
259 char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
260 BITMAPINFO *info = (BITMAPINFO *)buffer;
261 unsigned int width_bytes = (width + 31) / 32 * 4;
262 unsigned long *and_bits = NULL, *xor_bits;
263 unsigned long *data_bits;
264 int count, i;
265 CGColorSpaceRef colorspace;
266 CFMutableDataRef data;
267 CGDataProviderRef provider;
268 CGImageRef cgimage, cgmask, cgmasked;
269 CGPoint hot_spot;
270 CFDictionaryRef hot_spot_dict;
271 const CFStringRef keys[] = { CFSTR("image"), CFSTR("hotSpot") };
272 CFTypeRef values[ARRAY_SIZE(keys)];
273 CFDictionaryRef frame;
274 CFArrayRef frames;
276 TRACE("hdc %p icon->hbmMask %p icon->xHotspot %d icon->yHotspot %d width %d height %d\n",
277 hdc, icon->hbmMask, (int)icon->xHotspot, (int)icon->yHotspot, width, height);
279 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
280 info->bmiHeader.biWidth = width;
281 info->bmiHeader.biHeight = -height * 2;
282 info->bmiHeader.biPlanes = 1;
283 info->bmiHeader.biBitCount = 1;
284 info->bmiHeader.biCompression = BI_RGB;
285 info->bmiHeader.biSizeImage = width_bytes * height * 2;
286 info->bmiHeader.biXPelsPerMeter = 0;
287 info->bmiHeader.biYPelsPerMeter = 0;
288 info->bmiHeader.biClrUsed = 0;
289 info->bmiHeader.biClrImportant = 0;
291 and_bits = malloc(info->bmiHeader.biSizeImage);
292 if (!and_bits)
294 WARN("failed to allocate and_bits\n");
295 return NULL;
297 xor_bits = (unsigned long*)((char*)and_bits + info->bmiHeader.biSizeImage / 2);
299 if (!NtGdiGetDIBitsInternal(hdc, icon->hbmMask, 0, height * 2, and_bits, info, DIB_RGB_COLORS, 0, 0))
301 WARN("GetDIBits failed\n");
302 free(and_bits);
303 return NULL;
306 /* On Windows, the pixels of a monochrome cursor can have four effects:
307 draw black, draw white, leave unchanged (transparent), or invert. The Mac
308 only supports the first three. It can't do pixels which invert the
309 background. Since the background is usually white, I am arbitrarily
310 mapping "invert" to "draw black". This entails bitwise math between the
311 cursor's AND mask and XOR mask:
313 AND | XOR | Windows cursor pixel
314 --------------------------------
315 0 | 0 | black
316 0 | 1 | white
317 1 | 0 | transparent
318 1 | 1 | invert
320 AND | XOR | Mac image
321 ---------------------
322 0 | 0 | black (0)
323 0 | 1 | white (1)
324 1 | 0 | don't care
325 1 | 1 | black (0)
327 AND | XOR | Mac mask
328 ---------------------------
329 0 | 0 | paint (0)
330 0 | 1 | paint (0)
331 1 | 0 | don't paint (1)
332 1 | 1 | paint (0)
334 So, Mac image = AND ^ XOR and Mac mask = AND & ~XOR.
336 /* Create data for Mac image. */
337 data = CFDataCreateMutable(NULL, info->bmiHeader.biSizeImage / 2);
338 if (!data)
340 WARN("failed to create data\n");
341 free(and_bits);
342 return NULL;
345 /* image data = AND mask */
346 CFDataAppendBytes(data, (UInt8*)and_bits, info->bmiHeader.biSizeImage / 2);
347 /* image data ^= XOR mask */
348 data_bits = (unsigned long*)CFDataGetMutableBytePtr(data);
349 count = (info->bmiHeader.biSizeImage / 2) / sizeof(*data_bits);
350 for (i = 0; i < count; i++)
351 data_bits[i] ^= xor_bits[i];
353 colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGrayGamma2_2);
354 if (!colorspace)
356 WARN("failed to create colorspace\n");
357 CFRelease(data);
358 free(and_bits);
359 return NULL;
362 provider = CGDataProviderCreateWithCFData(data);
363 CFRelease(data);
364 if (!provider)
366 WARN("failed to create data provider\n");
367 CGColorSpaceRelease(colorspace);
368 free(and_bits);
369 return NULL;
372 cgimage = CGImageCreate(width, height, 1, 1, width_bytes, colorspace,
373 kCGImageAlphaNone | kCGBitmapByteOrderDefault,
374 provider, NULL, FALSE, kCGRenderingIntentDefault);
375 CGDataProviderRelease(provider);
376 CGColorSpaceRelease(colorspace);
377 if (!cgimage)
379 WARN("failed to create image\n");
380 free(and_bits);
381 return NULL;
384 /* Create data for mask. */
385 data = CFDataCreateMutable(NULL, info->bmiHeader.biSizeImage / 2);
386 if (!data)
388 WARN("failed to create data\n");
389 CGImageRelease(cgimage);
390 free(and_bits);
391 return NULL;
394 /* mask data = AND mask */
395 CFDataAppendBytes(data, (UInt8*)and_bits, info->bmiHeader.biSizeImage / 2);
396 /* mask data &= ~XOR mask */
397 data_bits = (unsigned long*)CFDataGetMutableBytePtr(data);
398 for (i = 0; i < count; i++)
399 data_bits[i] &= ~xor_bits[i];
400 free(and_bits);
402 provider = CGDataProviderCreateWithCFData(data);
403 CFRelease(data);
404 if (!provider)
406 WARN("failed to create data provider\n");
407 CGImageRelease(cgimage);
408 return NULL;
411 cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
412 CGDataProviderRelease(provider);
413 if (!cgmask)
415 WARN("failed to create mask image\n");
416 CGImageRelease(cgimage);
417 return NULL;
420 cgmasked = CGImageCreateWithMask(cgimage, cgmask);
421 CGImageRelease(cgimage);
422 CGImageRelease(cgmask);
423 if (!cgmasked)
425 WARN("failed to create masked image\n");
426 return NULL;
429 hot_spot = CGPointMake(icon->xHotspot, icon->yHotspot);
430 hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
431 if (!hot_spot_dict)
433 WARN("failed to create hot spot dictionary\n");
434 CGImageRelease(cgmasked);
435 return NULL;
438 values[0] = cgmasked;
439 values[1] = hot_spot_dict;
440 frame = CFDictionaryCreate(NULL, (const void**)keys, values, ARRAY_SIZE(keys),
441 &kCFCopyStringDictionaryKeyCallBacks,
442 &kCFTypeDictionaryValueCallBacks);
443 CFRelease(hot_spot_dict);
444 CGImageRelease(cgmasked);
445 if (!frame)
447 WARN("failed to create frame dictionary\n");
448 return NULL;
451 frames = CFArrayCreate(NULL, (const void**)&frame, 1, &kCFTypeArrayCallBacks);
452 CFRelease(frame);
453 if (!frames)
455 WARN("failed to create frames array\n");
456 return NULL;
459 return frames;
463 /***********************************************************************
464 * create_cursor_frame
466 * Create a frame dictionary for a cursor from a Windows icon.
467 * Keys:
468 * "image" a CGImage for the frame
469 * "duration" a CFNumber for the frame duration in seconds
470 * "hotSpot" a CFDictionary encoding a CGPoint for the hot spot
472 static CFDictionaryRef create_cursor_frame(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon,
473 HBITMAP hbmColor, unsigned char *color_bits, int color_size,
474 HBITMAP hbmMask, unsigned char *mask_bits, int mask_size,
475 int width, int height, int istep)
477 DWORD delay_jiffies, num_steps;
478 CFMutableDictionaryRef frame;
479 CGPoint hot_spot;
480 CFDictionaryRef hot_spot_dict;
481 double duration;
482 CFNumberRef duration_number;
483 CGImageRef cgimage;
485 TRACE("hdc %p iinfo->xHotspot %d iinfo->yHotspot %d icon %p hbmColor %p color_bits %p color_size %d"
486 " hbmMask %p mask_bits %p mask_size %d width %d height %d istep %d\n",
487 hdc, (int)iinfo->xHotspot, (int)iinfo->yHotspot, icon, hbmColor, color_bits, color_size,
488 hbmMask, mask_bits, mask_size, width, height, istep);
490 frame = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks,
491 &kCFTypeDictionaryValueCallBacks);
492 if (!frame)
494 WARN("failed to allocate dictionary for frame\n");
495 return NULL;
498 hot_spot = CGPointMake(iinfo->xHotspot, iinfo->yHotspot);
499 hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
500 if (!hot_spot_dict)
502 WARN("failed to create hot spot dictionary\n");
503 CFRelease(frame);
504 return NULL;
506 CFDictionarySetValue(frame, CFSTR("hotSpot"), hot_spot_dict);
507 CFRelease(hot_spot_dict);
509 if (NtUserGetCursorFrameInfo(icon, istep, &delay_jiffies, &num_steps) != 0)
510 duration = delay_jiffies / 60.0; /* convert jiffies (1/60s) to seconds */
511 else
513 WARN("Failed to retrieve animated cursor frame-rate for frame %d.\n", istep);
514 duration = 0.1; /* fallback delay, 100 ms */
516 duration_number = CFNumberCreate(NULL, kCFNumberDoubleType, &duration);
517 if (!duration_number)
519 WARN("failed to create duration number\n");
520 CFRelease(frame);
521 return NULL;
523 CFDictionarySetValue(frame, CFSTR("duration"), duration_number);
524 CFRelease(duration_number);
526 cgimage = create_cgimage_from_icon_bitmaps(hdc, icon, hbmColor, color_bits, color_size,
527 hbmMask, mask_bits, mask_size, width, height, istep);
528 if (!cgimage)
530 CFRelease(frame);
531 return NULL;
534 CFDictionarySetValue(frame, CFSTR("image"), cgimage);
535 CGImageRelease(cgimage);
537 return frame;
541 /***********************************************************************
542 * create_color_cursor
544 * Create an array of color cursor frames from a Windows cursor. Each
545 * frame is represented in the array by a dictionary.
546 * Frame dictionary keys:
547 * "image" a CGImage for the frame
548 * "duration" a CFNumber for the frame duration in seconds
549 * "hotSpot" a CFDictionary encoding a CGPoint for the hot spot
551 static CFArrayRef create_color_cursor(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon, int width, int height)
553 unsigned char *color_bits, *mask_bits;
554 HBITMAP hbmColor = 0, hbmMask = 0;
555 DWORD nFrames, delay_jiffies, i;
556 int color_size, mask_size;
557 BITMAPINFO *info = NULL;
558 CFMutableArrayRef frames;
560 TRACE("hdc %p iinfo %p icon %p width %d height %d\n", hdc, iinfo, icon, width, height);
562 /* Retrieve the number of frames to render */
563 if (!NtUserGetCursorFrameInfo(icon, 0, &delay_jiffies, &nFrames))
565 WARN("GetCursorFrameInfo failed\n");
566 return NULL;
568 if (!(frames = CFArrayCreateMutable(NULL, nFrames, &kCFTypeArrayCallBacks)))
570 WARN("failed to allocate frames array\n");
571 return NULL;
574 /* Allocate all of the resources necessary to obtain a cursor frame */
575 if (!(info = malloc(FIELD_OFFSET(BITMAPINFO, bmiColors[256])))) goto cleanup;
576 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
577 info->bmiHeader.biWidth = width;
578 info->bmiHeader.biHeight = -height;
579 info->bmiHeader.biPlanes = 1;
580 info->bmiHeader.biCompression = BI_RGB;
581 info->bmiHeader.biXPelsPerMeter = 0;
582 info->bmiHeader.biYPelsPerMeter = 0;
583 info->bmiHeader.biClrUsed = 0;
584 info->bmiHeader.biClrImportant = 0;
585 info->bmiHeader.biBitCount = 32;
586 color_size = width * height * 4;
587 info->bmiHeader.biSizeImage = color_size;
588 hbmColor = NtGdiCreateDIBSection(hdc, NULL, 0, info, DIB_RGB_COLORS,
589 0, 0, 0, (void **)&color_bits);
590 if (!hbmColor)
592 WARN("failed to create DIB section for cursor color data\n");
593 goto cleanup;
595 info->bmiHeader.biBitCount = 1;
596 info->bmiColors[0].rgbRed = 0;
597 info->bmiColors[0].rgbGreen = 0;
598 info->bmiColors[0].rgbBlue = 0;
599 info->bmiColors[0].rgbReserved = 0;
600 info->bmiColors[1].rgbRed = 0xff;
601 info->bmiColors[1].rgbGreen = 0xff;
602 info->bmiColors[1].rgbBlue = 0xff;
603 info->bmiColors[1].rgbReserved = 0;
605 mask_size = ((width + 31) / 32 * 4) * height; /* width_bytes * height */
606 info->bmiHeader.biSizeImage = mask_size;
607 hbmMask = NtGdiCreateDIBSection(hdc, NULL, 0, info, DIB_RGB_COLORS,
608 0, 0, 0, (void **)&mask_bits);
609 if (!hbmMask)
611 WARN("failed to create DIB section for cursor mask data\n");
612 goto cleanup;
615 /* Create a CFDictionary for each frame of the cursor */
616 for (i = 0; i < nFrames; i++)
618 CFDictionaryRef frame = create_cursor_frame(hdc, iinfo, icon,
619 hbmColor, color_bits, color_size,
620 hbmMask, mask_bits, mask_size,
621 width, height, i);
622 if (!frame) goto cleanup;
623 CFArrayAppendValue(frames, frame);
624 CFRelease(frame);
627 cleanup:
628 if (CFArrayGetCount(frames) < nFrames)
630 CFRelease(frames);
631 frames = NULL;
633 else
634 TRACE("returning cursor with %d frames\n", (int)nFrames);
635 /* Cleanup all of the resources used to obtain the frame data */
636 if (hbmColor) NtGdiDeleteObjectApp(hbmColor);
637 if (hbmMask) NtGdiDeleteObjectApp(hbmMask);
638 free(info);
639 return frames;
643 /***********************************************************************
644 * DestroyCursorIcon (MACDRV.@)
646 void macdrv_DestroyCursorIcon(HCURSOR cursor)
648 TRACE("cursor %p\n", cursor);
650 pthread_mutex_lock(&cursor_cache_mutex);
651 if (cursor_cache)
652 CFDictionaryRemoveValue(cursor_cache, cursor);
653 pthread_mutex_unlock(&cursor_cache_mutex);
657 /***********************************************************************
658 * ClipCursor (MACDRV.@)
660 * Set the cursor clipping rectangle.
662 BOOL macdrv_ClipCursor(LPCRECT clip)
664 CGRect rect;
666 TRACE("%s\n", wine_dbgstr_rect(clip));
668 if (clip)
670 rect = CGRectMake(clip->left, clip->top, max(1, clip->right - clip->left),
671 max(1, clip->bottom - clip->top));
673 else
674 rect = CGRectInfinite;
676 /* FIXME: This needs to be done not just in this process but in all of the
677 ones for this WINEPREFIX. Broadcast a message to do that. */
679 return macdrv_clip_cursor(rect);
683 /***********************************************************************
684 * GetCursorPos (MACDRV.@)
686 BOOL macdrv_GetCursorPos(LPPOINT pos)
688 CGPoint pt;
689 BOOL ret;
691 ret = macdrv_get_cursor_position(&pt);
692 if (ret)
694 TRACE("pointer at (%g,%g) server pos %d,%d\n", pt.x, pt.y, (int)pos->x, (int)pos->y);
695 pos->x = floor(pt.x);
696 pos->y = floor(pt.y);
698 return ret;
702 /***********************************************************************
703 * SetCapture (MACDRV.@)
705 void macdrv_SetCapture(HWND hwnd, UINT flags)
707 struct macdrv_thread_data *thread_data = macdrv_thread_data();
708 HWND top = NtUserGetAncestor(hwnd, GA_ROOT);
709 macdrv_window cocoa_window = macdrv_get_cocoa_window(top, FALSE);
711 TRACE("hwnd %p top %p/%p flags 0x%08x\n", hwnd, top, cocoa_window, flags);
713 if (!thread_data) return;
715 thread_data->capture_window = cocoa_window;
716 macdrv_set_mouse_capture_window(cocoa_window);
720 static BOOL get_icon_info(HICON handle, ICONINFOEXW *ret)
722 UNICODE_STRING module, res_name;
723 ICONINFO info;
725 module.Buffer = ret->szModName;
726 module.MaximumLength = sizeof(ret->szModName) - sizeof(WCHAR);
727 res_name.Buffer = ret->szResName;
728 res_name.MaximumLength = sizeof(ret->szResName) - sizeof(WCHAR);
729 if (!NtUserGetIconInfo(handle, &info, &module, &res_name, NULL, 0)) return FALSE;
730 ret->fIcon = info.fIcon;
731 ret->xHotspot = info.xHotspot;
732 ret->yHotspot = info.yHotspot;
733 ret->hbmColor = info.hbmColor;
734 ret->hbmMask = info.hbmMask;
735 ret->wResID = res_name.Length ? 0 : LOWORD(res_name.Buffer);
736 ret->szModName[module.Length] = 0;
737 ret->szResName[res_name.Length] = 0;
738 return TRUE;
742 /***********************************************************************
743 * SetCursor (MACDRV.@)
745 void macdrv_SetCursor(HCURSOR cursor)
747 CFStringRef cursor_name = NULL;
748 CFArrayRef cursor_frames = NULL;
750 TRACE("%p\n", cursor);
752 if (cursor)
754 ICONINFOEXW info;
756 pthread_mutex_lock(&cursor_cache_mutex);
757 if (cursor_cache)
759 CFTypeRef cached_cursor = CFDictionaryGetValue(cursor_cache, cursor);
760 if (cached_cursor)
762 if (CFGetTypeID(cached_cursor) == CFStringGetTypeID())
763 cursor_name = CFRetain(cached_cursor);
764 else
765 cursor_frames = CFRetain(cached_cursor);
768 pthread_mutex_unlock(&cursor_cache_mutex);
769 if (cursor_name || cursor_frames)
770 goto done;
772 info.cbSize = sizeof(info);
773 if (!get_icon_info(cursor, &info))
775 WARN("GetIconInfoExW failed\n");
776 return;
779 if ((cursor_name = copy_system_cursor_name(&info)))
781 NtGdiDeleteObjectApp(info.hbmColor);
782 NtGdiDeleteObjectApp(info.hbmMask);
784 else
786 BITMAP bm;
787 HDC hdc;
789 NtGdiExtGetObjectW(info.hbmMask, sizeof(bm), &bm);
790 if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
792 /* make sure hotspot is valid */
793 if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
795 info.xHotspot = bm.bmWidth / 2;
796 info.yHotspot = bm.bmHeight / 2;
799 hdc = NtGdiCreateCompatibleDC(0);
801 if (info.hbmColor)
803 cursor_frames = create_color_cursor(hdc, &info, cursor, bm.bmWidth, bm.bmHeight);
804 NtGdiDeleteObjectApp(info.hbmColor);
806 else
807 cursor_frames = create_monochrome_cursor(hdc, &info, bm.bmWidth, bm.bmHeight);
809 NtGdiDeleteObjectApp(info.hbmMask);
810 NtGdiDeleteObjectApp(hdc);
813 if (cursor_name || cursor_frames)
815 pthread_mutex_lock(&cursor_cache_mutex);
816 if (!cursor_cache)
817 cursor_cache = CFDictionaryCreateMutable(NULL, 0, NULL,
818 &kCFTypeDictionaryValueCallBacks);
819 CFDictionarySetValue(cursor_cache, cursor,
820 cursor_name ? (CFTypeRef)cursor_name : (CFTypeRef)cursor_frames);
821 pthread_mutex_unlock(&cursor_cache_mutex);
823 else
824 cursor_name = CFRetain(CFSTR("arrowCursor"));
827 done:
828 TRACE("setting cursor with cursor_name %s cursor_frames %p\n", debugstr_cf(cursor_name), cursor_frames);
829 macdrv_set_cursor(cursor_name, cursor_frames);
830 if (cursor_name) CFRelease(cursor_name);
831 if (cursor_frames) CFRelease(cursor_frames);
835 /***********************************************************************
836 * SetCursorPos (MACDRV.@)
838 BOOL macdrv_SetCursorPos(INT x, INT y)
840 BOOL ret = macdrv_set_cursor_position(CGPointMake(x, y));
841 if (ret)
842 TRACE("warped to %d,%d\n", x, y);
843 else
844 ERR("failed to warp to %d,%d\n", x, y);
845 return ret;
849 /***********************************************************************
850 * macdrv_mouse_button
852 * Handler for MOUSE_BUTTON events.
854 void macdrv_mouse_button(HWND hwnd, const macdrv_event *event)
856 UINT flags = 0;
857 WORD data = 0;
859 TRACE("win %p button %d %s at (%d,%d) time %lu (%lu ticks ago)\n", hwnd, event->mouse_button.button,
860 (event->mouse_button.pressed ? "pressed" : "released"),
861 event->mouse_button.x, event->mouse_button.y,
862 event->mouse_button.time_ms, (NtGetTickCount() - event->mouse_button.time_ms));
864 if (event->mouse_button.pressed)
866 switch (event->mouse_button.button)
868 case 0: flags |= MOUSEEVENTF_LEFTDOWN; break;
869 case 1: flags |= MOUSEEVENTF_RIGHTDOWN; break;
870 case 2: flags |= MOUSEEVENTF_MIDDLEDOWN; break;
871 default:
872 flags |= MOUSEEVENTF_XDOWN;
873 data = 1 << (event->mouse_button.button - 3);
874 break;
877 else
879 switch (event->mouse_button.button)
881 case 0: flags |= MOUSEEVENTF_LEFTUP; break;
882 case 1: flags |= MOUSEEVENTF_RIGHTUP; break;
883 case 2: flags |= MOUSEEVENTF_MIDDLEUP; break;
884 default:
885 flags |= MOUSEEVENTF_XUP;
886 data = 1 << (event->mouse_button.button - 3);
887 break;
891 send_mouse_input(hwnd, event->window, flags | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
892 event->mouse_button.x, event->mouse_button.y,
893 data, FALSE, event->mouse_button.time_ms);
897 /***********************************************************************
898 * macdrv_mouse_moved
900 * Handler for MOUSE_MOVED_RELATIVE and MOUSE_MOVED_ABSOLUTE events.
902 void macdrv_mouse_moved(HWND hwnd, const macdrv_event *event)
904 UINT flags = MOUSEEVENTF_MOVE;
906 TRACE("win %p/%p %s (%d,%d) drag %d time %lu (%lu ticks ago)\n", hwnd, event->window,
907 (event->type == MOUSE_MOVED_RELATIVE) ? "relative" : "absolute",
908 event->mouse_moved.x, event->mouse_moved.y, event->mouse_moved.drag,
909 event->mouse_moved.time_ms, (NtGetTickCount() - event->mouse_moved.time_ms));
911 if (event->type == MOUSE_MOVED_ABSOLUTE)
912 flags |= MOUSEEVENTF_ABSOLUTE;
914 send_mouse_input(hwnd, event->window, flags, event->mouse_moved.x, event->mouse_moved.y,
915 0, event->mouse_moved.drag, event->mouse_moved.time_ms);
919 /***********************************************************************
920 * macdrv_mouse_scroll
922 * Handler for MOUSE_SCROLL events.
924 void macdrv_mouse_scroll(HWND hwnd, const macdrv_event *event)
926 TRACE("win %p/%p scroll (%d,%d) at (%d,%d) time %lu (%lu ticks ago)\n", hwnd,
927 event->window, event->mouse_scroll.x_scroll, event->mouse_scroll.y_scroll,
928 event->mouse_scroll.x, event->mouse_scroll.y,
929 event->mouse_scroll.time_ms, (NtGetTickCount() - event->mouse_scroll.time_ms));
931 if (event->mouse_scroll.y_scroll)
932 send_mouse_input(hwnd, event->window, MOUSEEVENTF_WHEEL | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
933 event->mouse_scroll.x, event->mouse_scroll.y,
934 event->mouse_scroll.y_scroll, FALSE, event->mouse_scroll.time_ms);
935 if (event->mouse_scroll.x_scroll)
936 send_mouse_input(hwnd, event->window, MOUSEEVENTF_HWHEEL | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
937 event->mouse_scroll.x, event->mouse_scroll.y,
938 event->mouse_scroll.x_scroll, FALSE, event->mouse_scroll.time_ms);
942 /***********************************************************************
943 * macdrv_release_capture
945 * Handler for RELEASE_CAPTURE events.
947 void macdrv_release_capture(HWND hwnd, const macdrv_event *event)
949 struct macdrv_thread_data *thread_data = macdrv_thread_data();
950 HWND capture = get_capture();
951 HWND capture_top = NtUserGetAncestor(capture, GA_ROOT);
953 TRACE("win %p/%p thread_data->capture_window %p GetCapture() %p in %p\n", hwnd,
954 event->window, thread_data->capture_window, capture, capture_top);
956 if (event->window == thread_data->capture_window && hwnd == capture_top)
958 NtUserReleaseCapture();
959 if (!NtUserPostMessage(capture, WM_CANCELMODE, 0, 0))
960 WARN("failed to post WM_CANCELMODE; error 0x%08x\n", (unsigned int)RtlGetLastWin32Error());