winemac: Add support for "query" events which wait for synchronous responses.
[wine] / dlls / winemac.drv / mouse.c
1 /*
2  * MACDRV mouse driver
3  *
4  * Copyright 1998 Ulrich Weigand
5  * Copyright 2007 Henri Verbeet
6  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24
25 #include "macdrv.h"
26 #define OEMRESOURCE
27 #include "winuser.h"
28 #include "winreg.h"
29 #include "wine/server.h"
30 #include "wine/unicode.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
33
34
35 static CRITICAL_SECTION cursor_cache_section;
36 static CRITICAL_SECTION_DEBUG critsect_debug =
37 {
38     0, 0, &cursor_cache_section,
39     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
40       0, 0, { (DWORD_PTR)(__FILE__ ": cursor_cache_section") }
41 };
42 static CRITICAL_SECTION cursor_cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
43
44 static CFMutableDictionaryRef cursor_cache;
45
46
47 struct system_cursors
48 {
49     WORD id;
50     CFStringRef name;
51 };
52
53 static const struct system_cursors user32_cursors[] =
54 {
55     { OCR_NORMAL,      CFSTR("arrowCursor") },
56     { OCR_IBEAM,       CFSTR("IBeamCursor") },
57     { OCR_CROSS,       CFSTR("crosshairCursor") },
58     { OCR_SIZEWE,      CFSTR("resizeLeftRightCursor") },
59     { OCR_SIZENS,      CFSTR("resizeUpDownCursor") },
60     { OCR_NO,          CFSTR("operationNotAllowedCursor") },
61     { OCR_HAND,        CFSTR("pointingHandCursor") },
62     { 0 }
63 };
64
65 static const struct system_cursors comctl32_cursors[] =
66 {
67     { 102, CFSTR("closedHandCursor") },
68     { 104, CFSTR("dragCopyCursor") },
69     { 105, CFSTR("arrowCursor") },
70     { 106, CFSTR("resizeLeftRightCursor") },
71     { 107, CFSTR("resizeLeftRightCursor") },
72     { 108, CFSTR("pointingHandCursor") },
73     { 135, CFSTR("resizeUpDownCursor") },
74     { 0 }
75 };
76
77 static const struct system_cursors ole32_cursors[] =
78 {
79     { 1, CFSTR("operationNotAllowedCursor") },
80     { 2, CFSTR("closedHandCursor") },
81     { 3, CFSTR("dragCopyCursor") },
82     { 4, CFSTR("dragLinkCursor") },
83     { 0 }
84 };
85
86 static const struct system_cursors riched20_cursors[] =
87 {
88     { 105, CFSTR("pointingHandCursor") },
89     { 109, CFSTR("dragCopyCursor") },
90     { 110, CFSTR("closedHandCursor") },
91     { 111, CFSTR("operationNotAllowedCursor") },
92     { 0 }
93 };
94
95 static const struct
96 {
97     const struct system_cursors *cursors;
98     WCHAR name[16];
99 } module_cursors[] =
100 {
101     { user32_cursors, {'u','s','e','r','3','2','.','d','l','l',0} },
102     { comctl32_cursors, {'c','o','m','c','t','l','3','2','.','d','l','l',0} },
103     { ole32_cursors, {'o','l','e','3','2','.','d','l','l',0} },
104     { riched20_cursors, {'r','i','c','h','e','d','2','0','.','d','l','l',0} }
105 };
106
107 /* The names of NSCursor class methods which return cursor objects. */
108 static const CFStringRef cocoa_cursor_names[] =
109 {
110     CFSTR("arrowCursor"),
111     CFSTR("closedHandCursor"),
112     CFSTR("contextualMenuCursor"),
113     CFSTR("crosshairCursor"),
114     CFSTR("disappearingItemCursor"),
115     CFSTR("dragCopyCursor"),
116     CFSTR("dragLinkCursor"),
117     CFSTR("IBeamCursor"),
118     CFSTR("IBeamCursorForVerticalLayout"),
119     CFSTR("openHandCursor"),
120     CFSTR("operationNotAllowedCursor"),
121     CFSTR("pointingHandCursor"),
122     CFSTR("resizeDownCursor"),
123     CFSTR("resizeLeftCursor"),
124     CFSTR("resizeLeftRightCursor"),
125     CFSTR("resizeRightCursor"),
126     CFSTR("resizeUpCursor"),
127     CFSTR("resizeUpDownCursor"),
128 };
129
130
131 /***********************************************************************
132  *              send_mouse_input
133  *
134  * Update the various window states on a mouse event.
135  */
136 static void send_mouse_input(HWND hwnd, UINT flags, int x, int y,
137                              DWORD mouse_data, unsigned long time)
138 {
139     INPUT input;
140     HWND top_level_hwnd;
141
142     top_level_hwnd = GetAncestor(hwnd, GA_ROOT);
143
144     if ((flags & MOUSEEVENTF_MOVE) && (flags & MOUSEEVENTF_ABSOLUTE))
145     {
146         RECT rect;
147
148         /* update the wine server Z-order */
149         SetRect(&rect, x, y, x + 1, y + 1);
150         MapWindowPoints(0, top_level_hwnd, (POINT *)&rect, 2);
151
152         SERVER_START_REQ(update_window_zorder)
153         {
154             req->window      = wine_server_user_handle(top_level_hwnd);
155             req->rect.left   = rect.left;
156             req->rect.top    = rect.top;
157             req->rect.right  = rect.right;
158             req->rect.bottom = rect.bottom;
159             wine_server_call(req);
160         }
161         SERVER_END_REQ;
162     }
163
164     input.type              = INPUT_MOUSE;
165     input.mi.dx             = x;
166     input.mi.dy             = y;
167     input.mi.mouseData      = mouse_data;
168     input.mi.dwFlags        = flags;
169     input.mi.time           = time;
170     input.mi.dwExtraInfo    = 0;
171
172     __wine_send_input(top_level_hwnd, &input);
173 }
174
175
176 /***********************************************************************
177  *              copy_system_cursor_name
178  */
179 CFStringRef copy_system_cursor_name(ICONINFOEXW *info)
180 {
181     static const WCHAR idW[] = {'%','h','u',0};
182     const struct system_cursors *cursors;
183     unsigned int i;
184     CFStringRef cursor_name = NULL;
185     HMODULE module;
186     HKEY key;
187     WCHAR *p, name[MAX_PATH * 2];
188
189     TRACE("info->szModName %s info->szResName %s info->wResID %hu\n", debugstr_w(info->szModName),
190           debugstr_w(info->szResName), info->wResID);
191
192     if (!info->szModName[0]) return NULL;
193
194     p = strrchrW(info->szModName, '\\');
195     strcpyW(name, p ? p + 1 : info->szModName);
196     p = name + strlenW(name);
197     *p++ = ',';
198     if (info->szResName[0]) strcpyW(p, info->szResName);
199     else sprintfW(p, idW, info->wResID);
200
201     /* @@ Wine registry key: HKCU\Software\Wine\Mac Driver\Cursors */
202     if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Mac Driver\\Cursors", &key))
203     {
204         WCHAR value[64];
205         DWORD size, ret;
206
207         value[0] = 0;
208         size = sizeof(value) / sizeof(WCHAR);
209         ret = RegQueryValueExW(key, name, NULL, NULL, (BYTE *)value, &size);
210         RegCloseKey(key);
211         if (!ret)
212         {
213             if (!value[0])
214             {
215                 TRACE("registry forces standard cursor for %s\n", debugstr_w(name));
216                 return NULL; /* force standard cursor */
217             }
218
219             cursor_name = CFStringCreateWithCharacters(NULL, value, strlenW(value));
220             if (!cursor_name)
221             {
222                 WARN("CFStringCreateWithCharacters failed for %s\n", debugstr_w(value));
223                 return NULL;
224             }
225
226             /* Make sure it's one of the appropriate NSCursor class methods. */
227             for (i = 0; i < sizeof(cocoa_cursor_names) / sizeof(cocoa_cursor_names[0]); i++)
228                 if (CFEqual(cursor_name, cocoa_cursor_names[i]))
229                     goto done;
230
231             WARN("%s mapped to invalid Cocoa cursor name %s\n", debugstr_w(name), debugstr_w(value));
232             CFRelease(cursor_name);
233             return NULL;
234         }
235     }
236
237     if (info->szResName[0]) goto done;  /* only integer resources are supported here */
238     if (!(module = GetModuleHandleW(info->szModName))) goto done;
239
240     for (i = 0; i < sizeof(module_cursors)/sizeof(module_cursors[0]); i++)
241         if (GetModuleHandleW(module_cursors[i].name) == module) break;
242     if (i == sizeof(module_cursors)/sizeof(module_cursors[0])) goto done;
243
244     cursors = module_cursors[i].cursors;
245     for (i = 0; cursors[i].id; i++)
246         if (cursors[i].id == info->wResID)
247         {
248             cursor_name = CFRetain(cursors[i].name);
249             break;
250         }
251
252 done:
253     if (cursor_name)
254         TRACE("%s -> %s\n", debugstr_w(name), debugstr_cf(cursor_name));
255     else
256         WARN("no system cursor found for %s\n", debugstr_w(name));
257     return cursor_name;
258 }
259
260 /***********************************************************************
261  *              create_monochrome_cursor
262  */
263 CFArrayRef create_monochrome_cursor(HDC hdc, const ICONINFOEXW *icon, int width, int height)
264 {
265     char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
266     BITMAPINFO *info = (BITMAPINFO *)buffer;
267     unsigned int width_bytes = (width + 31) / 32 * 4;
268     unsigned long *and_bits = NULL, *xor_bits;
269     unsigned long *data_bits;
270     int count, i;
271     CGColorSpaceRef colorspace;
272     CFMutableDataRef data;
273     CGDataProviderRef provider;
274     CGImageRef cgimage, cgmask, cgmasked;
275     CGPoint hot_spot;
276     CFDictionaryRef hot_spot_dict;
277     const CFStringRef keys[] = { CFSTR("image"), CFSTR("hotSpot") };
278     CFTypeRef values[sizeof(keys) / sizeof(keys[0])];
279     CFDictionaryRef frame;
280     CFArrayRef frames;
281
282     TRACE("hdc %p icon->hbmMask %p icon->xHotspot %d icon->yHotspot %d width %d height %d\n",
283           hdc, icon->hbmMask, icon->xHotspot, icon->yHotspot, width, height);
284
285     info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
286     info->bmiHeader.biWidth = width;
287     info->bmiHeader.biHeight = -height * 2;
288     info->bmiHeader.biPlanes = 1;
289     info->bmiHeader.biBitCount = 1;
290     info->bmiHeader.biCompression = BI_RGB;
291     info->bmiHeader.biSizeImage = width_bytes * height * 2;
292     info->bmiHeader.biXPelsPerMeter = 0;
293     info->bmiHeader.biYPelsPerMeter = 0;
294     info->bmiHeader.biClrUsed = 0;
295     info->bmiHeader.biClrImportant = 0;
296
297     and_bits = HeapAlloc(GetProcessHeap(), 0, info->bmiHeader.biSizeImage);
298     if (!and_bits)
299     {
300         WARN("failed to allocate and_bits\n");
301         return NULL;
302     }
303     xor_bits = (unsigned long*)((char*)and_bits + info->bmiHeader.biSizeImage / 2);
304
305     if (!GetDIBits(hdc, icon->hbmMask, 0, height * 2, and_bits, info, DIB_RGB_COLORS))
306     {
307         WARN("GetDIBits failed\n");
308         HeapFree(GetProcessHeap(), 0, and_bits);
309         return NULL;
310     }
311
312     /* On Windows, the pixels of a monochrome cursor can have four effects:
313        draw black, draw white, leave unchanged (transparent), or invert.  The Mac
314        only supports the first three.  It can't do pixels which invert the
315        background.  Since the background is usually white, I am arbitrarily
316        mapping "invert" to "draw black".  This entails bitwise math between the
317        cursor's AND mask and XOR mask:
318
319             AND | XOR | Windows cursor pixel
320             --------------------------------
321              0  |  0  | black
322              0  |  1  | white
323              1  |  0  | transparent
324              1  |  1  | invert
325
326             AND | XOR | Mac image
327             ---------------------
328              0  |  0  | black (0)
329              0  |  1  | white (1)
330              1  |  0  | don't care
331              1  |  1  | black (0)
332
333             AND | XOR | Mac mask
334             ---------------------------
335              0  |  0  | paint (0)
336              0  |  1  | paint (0)
337              1  |  0  | don't paint (1)
338              1  |  1  | paint (0)
339
340        So, Mac image = AND ^ XOR and Mac mask = AND & ~XOR.
341       */
342     /* Create data for Mac image. */
343     data = CFDataCreateMutable(NULL, info->bmiHeader.biSizeImage / 2);
344     if (!data)
345     {
346         WARN("failed to create data\n");
347         HeapFree(GetProcessHeap(), 0, and_bits);
348         return NULL;
349     }
350
351     /* image data = AND mask */
352     CFDataAppendBytes(data, (UInt8*)and_bits, info->bmiHeader.biSizeImage / 2);
353     /* image data ^= XOR mask */
354     data_bits = (unsigned long*)CFDataGetMutableBytePtr(data);
355     count = (info->bmiHeader.biSizeImage / 2) / sizeof(*data_bits);
356     for (i = 0; i < count; i++)
357         data_bits[i] ^= xor_bits[i];
358
359     colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
360     if (!colorspace)
361     {
362         WARN("failed to create colorspace\n");
363         CFRelease(data);
364         HeapFree(GetProcessHeap(), 0, and_bits);
365         return NULL;
366     }
367
368     provider = CGDataProviderCreateWithCFData(data);
369     CFRelease(data);
370     if (!provider)
371     {
372         WARN("failed to create data provider\n");
373         CGColorSpaceRelease(colorspace);
374         HeapFree(GetProcessHeap(), 0, and_bits);
375         return NULL;
376     }
377
378     cgimage = CGImageCreate(width, height, 1, 1, width_bytes, colorspace,
379                             kCGImageAlphaNone | kCGBitmapByteOrderDefault,
380                             provider, NULL, FALSE, kCGRenderingIntentDefault);
381     CGDataProviderRelease(provider);
382     CGColorSpaceRelease(colorspace);
383     if (!cgimage)
384     {
385         WARN("failed to create image\n");
386         HeapFree(GetProcessHeap(), 0, and_bits);
387         return NULL;
388     }
389
390     /* Create data for mask. */
391     data = CFDataCreateMutable(NULL, info->bmiHeader.biSizeImage / 2);
392     if (!data)
393     {
394         WARN("failed to create data\n");
395         CGImageRelease(cgimage);
396         HeapFree(GetProcessHeap(), 0, and_bits);
397         return NULL;
398     }
399
400     /* mask data = AND mask */
401     CFDataAppendBytes(data, (UInt8*)and_bits, info->bmiHeader.biSizeImage / 2);
402     /* mask data &= ~XOR mask */
403     data_bits = (unsigned long*)CFDataGetMutableBytePtr(data);
404     for (i = 0; i < count; i++)
405         data_bits[i] &= ~xor_bits[i];
406     HeapFree(GetProcessHeap(), 0, and_bits);
407
408     provider = CGDataProviderCreateWithCFData(data);
409     CFRelease(data);
410     if (!provider)
411     {
412         WARN("failed to create data provider\n");
413         CGImageRelease(cgimage);
414         return NULL;
415     }
416
417     cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
418     CGDataProviderRelease(provider);
419     if (!cgmask)
420     {
421         WARN("failed to create mask image\n");
422         CGImageRelease(cgimage);
423         return NULL;
424     }
425
426     cgmasked = CGImageCreateWithMask(cgimage, cgmask);
427     CGImageRelease(cgimage);
428     CGImageRelease(cgmask);
429     if (!cgmasked)
430     {
431         WARN("failed to create masked image\n");
432         return NULL;
433     }
434
435     hot_spot = CGPointMake(icon->xHotspot, icon->yHotspot);
436     hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
437     if (!hot_spot_dict)
438     {
439         WARN("failed to create hot spot dictionary\n");
440         CGImageRelease(cgmasked);
441         return NULL;
442     }
443
444     values[0] = cgmasked;
445     values[1] = hot_spot_dict;
446     frame = CFDictionaryCreate(NULL, (const void**)keys, values, sizeof(keys) / sizeof(keys[0]),
447                                &kCFCopyStringDictionaryKeyCallBacks,
448                                &kCFTypeDictionaryValueCallBacks);
449     CFRelease(hot_spot_dict);
450     CGImageRelease(cgmasked);
451     if (!frame)
452     {
453         WARN("failed to create frame dictionary\n");
454         return NULL;
455     }
456
457     frames = CFArrayCreate(NULL, (const void**)&frame, 1, &kCFTypeArrayCallBacks);
458     CFRelease(frame);
459     if (!frames)
460     {
461         WARN("failed to create frames array\n");
462         return NULL;
463     }
464
465     return frames;
466 }
467
468
469 /***********************************************************************
470  *              create_cgimage_from_icon
471  */
472 static CGImageRef create_cgimage_from_icon(HDC hdc, HANDLE icon, HBITMAP hbmColor,
473                                            unsigned char *color_bits, int color_size, HBITMAP hbmMask,
474                                            unsigned char *mask_bits, int mask_size, int width,
475                                            int height, int istep)
476 {
477     int i, has_alpha = FALSE;
478     DWORD *ptr;
479     CGBitmapInfo alpha_format;
480     CGColorSpaceRef colorspace;
481     CFDataRef data;
482     CGDataProviderRef provider;
483     CGImageRef cgimage;
484
485     /* draw the cursor frame to a temporary buffer then create a CGImage from that */
486     memset(color_bits, 0x00, color_size);
487     SelectObject(hdc, hbmColor);
488     if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_NORMAL))
489     {
490         WARN("Could not draw frame %d (walk past end of frames).\n", istep);
491         return NULL;
492     }
493
494     /* check if the cursor frame was drawn with an alpha channel */
495     for (i = 0, ptr = (DWORD*)color_bits; i < width * height; i++, ptr++)
496         if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
497
498     if (has_alpha)
499         alpha_format = kCGImageAlphaFirst;
500     else
501         alpha_format = kCGImageAlphaNoneSkipFirst;
502
503     colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
504     if (!colorspace)
505     {
506         WARN("failed to create colorspace\n");
507         return NULL;
508     }
509
510     data = CFDataCreate(NULL, (UInt8*)color_bits, color_size);
511     if (!data)
512     {
513         WARN("failed to create data\n");
514         CGColorSpaceRelease(colorspace);
515         return NULL;
516     }
517
518     provider = CGDataProviderCreateWithCFData(data);
519     CFRelease(data);
520     if (!provider)
521     {
522         WARN("failed to create data provider\n");
523         CGColorSpaceRelease(colorspace);
524         return NULL;
525     }
526
527     cgimage = CGImageCreate(width, height, 8, 32, width * 4, colorspace,
528                             alpha_format | kCGBitmapByteOrder32Little,
529                             provider, NULL, FALSE, kCGRenderingIntentDefault);
530     CGDataProviderRelease(provider);
531     CGColorSpaceRelease(colorspace);
532     if (!cgimage)
533     {
534         WARN("failed to create image\n");
535         return NULL;
536     }
537
538     /* if no alpha channel was drawn then generate it from the mask */
539     if (!has_alpha)
540     {
541         unsigned int width_bytes = (width + 31) / 32 * 4;
542         CGImageRef cgmask, temp;
543
544         /* draw the cursor mask to a temporary buffer */
545         memset(mask_bits, 0xFF, mask_size);
546         SelectObject(hdc, hbmMask);
547         if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_MASK))
548         {
549             WARN("Failed to draw frame mask %d.\n", istep);
550             CGImageRelease(cgimage);
551             return NULL;
552         }
553
554         data = CFDataCreate(NULL, (UInt8*)mask_bits, mask_size);
555         if (!data)
556         {
557             WARN("failed to create data\n");
558             CGImageRelease(cgimage);
559             return NULL;
560         }
561
562         provider = CGDataProviderCreateWithCFData(data);
563         CFRelease(data);
564         if (!provider)
565         {
566             WARN("failed to create data provider\n");
567             CGImageRelease(cgimage);
568             return NULL;
569         }
570
571         cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
572         CGDataProviderRelease(provider);
573         if (!cgmask)
574         {
575             WARN("failed to create mask\n");
576             CGImageRelease(cgimage);
577             return NULL;
578         }
579
580         temp = CGImageCreateWithMask(cgimage, cgmask);
581         CGImageRelease(cgmask);
582         CGImageRelease(cgimage);
583         if (!temp)
584         {
585             WARN("failed to create masked image\n");
586             return NULL;
587         }
588         cgimage = temp;
589     }
590
591     return cgimage;
592 }
593
594
595 /***********************************************************************
596  *              create_cursor_frame
597  *
598  * Create a frame dictionary for a cursor from a Windows icon.
599  * Keys:
600  *      "image"     a CGImage for the frame
601  *      "duration"  a CFNumber for the frame duration in seconds
602  *      "hotSpot"   a CFDictionary encoding a CGPoint for the hot spot
603  */
604 static CFDictionaryRef create_cursor_frame(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon,
605                                            HBITMAP hbmColor, unsigned char *color_bits, int color_size,
606                                            HBITMAP hbmMask, unsigned char *mask_bits, int mask_size,
607                                            int width, int height, int istep)
608 {
609     DWORD delay_jiffies, num_steps;
610     CFMutableDictionaryRef frame;
611     CGPoint hot_spot;
612     CFDictionaryRef hot_spot_dict;
613     double duration;
614     CFNumberRef duration_number;
615     CGImageRef cgimage;
616
617     TRACE("hdc %p iinfo->xHotspot %d iinfo->yHotspot %d icon %p hbmColor %p color_bits %p color_size %d"
618           " hbmMask %p mask_bits %p mask_size %d width %d height %d istep %d\n",
619           hdc, iinfo->xHotspot, iinfo->yHotspot, icon, hbmColor, color_bits, color_size,
620           hbmMask, mask_bits, mask_size, width, height, istep);
621
622     frame = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks,
623                                       &kCFTypeDictionaryValueCallBacks);
624     if (!frame)
625     {
626         WARN("failed to allocate dictionary for frame\n");
627         return NULL;
628     }
629
630     hot_spot = CGPointMake(iinfo->xHotspot, iinfo->yHotspot);
631     hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
632     if (!hot_spot_dict)
633     {
634         WARN("failed to create hot spot dictionary\n");
635         CFRelease(frame);
636         return NULL;
637     }
638     CFDictionarySetValue(frame, CFSTR("hotSpot"), hot_spot_dict);
639     CFRelease(hot_spot_dict);
640
641     if (GetCursorFrameInfo(icon, 0x0 /* unknown parameter */, istep, &delay_jiffies, &num_steps) != 0)
642         duration = delay_jiffies / 60.0; /* convert jiffies (1/60s) to seconds */
643     else
644     {
645         WARN("Failed to retrieve animated cursor frame-rate for frame %d.\n", istep);
646         duration = 0.1; /* fallback delay, 100 ms */
647     }
648     duration_number = CFNumberCreate(NULL, kCFNumberDoubleType, &duration);
649     if (!duration_number)
650     {
651         WARN("failed to create duration number\n");
652         CFRelease(frame);
653         return NULL;
654     }
655     CFDictionarySetValue(frame, CFSTR("duration"), duration_number);
656     CFRelease(duration_number);
657
658     cgimage = create_cgimage_from_icon(hdc, icon, hbmColor, color_bits, color_size,
659                                        hbmMask, mask_bits, mask_size, width, height, istep);
660     if (!cgimage)
661     {
662         CFRelease(frame);
663         return NULL;
664     }
665
666     CFDictionarySetValue(frame, CFSTR("image"), cgimage);
667     CGImageRelease(cgimage);
668
669     return frame;
670 }
671
672
673 /***********************************************************************
674  *              create_color_cursor
675  *
676  * Create an array of color cursor frames from a Windows cursor.  Each
677  * frame is represented in the array by a dictionary.
678  * Frame dictionary keys:
679  *      "image"     a CGImage for the frame
680  *      "duration"  a CFNumber for the frame duration in seconds
681  *      "hotSpot"   a CFDictionary encoding a CGPoint for the hot spot
682  */
683 static CFArrayRef create_color_cursor(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon, int width, int height)
684 {
685     unsigned char *color_bits, *mask_bits;
686     HBITMAP hbmColor = 0, hbmMask = 0;
687     DWORD nFrames, delay_jiffies, i;
688     int color_size, mask_size;
689     BITMAPINFO *info = NULL;
690     CFMutableArrayRef frames;
691
692     TRACE("hdc %p iinfo %p icon %p width %d height %d\n", hdc, iinfo, icon, width, height);
693
694     /* Retrieve the number of frames to render */
695     if (!GetCursorFrameInfo(icon, 0x0 /* unknown parameter */, 0, &delay_jiffies, &nFrames))
696     {
697         WARN("GetCursorFrameInfo failed\n");
698         return NULL;
699     }
700     if (!(frames = CFArrayCreateMutable(NULL, nFrames, &kCFTypeArrayCallBacks)))
701     {
702         WARN("failed to allocate frames array\n");
703         return NULL;
704     }
705
706     /* Allocate all of the resources necessary to obtain a cursor frame */
707     if (!(info = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(BITMAPINFO, bmiColors[256])))) goto cleanup;
708     info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
709     info->bmiHeader.biWidth = width;
710     info->bmiHeader.biHeight = -height;
711     info->bmiHeader.biPlanes = 1;
712     info->bmiHeader.biCompression = BI_RGB;
713     info->bmiHeader.biXPelsPerMeter = 0;
714     info->bmiHeader.biYPelsPerMeter = 0;
715     info->bmiHeader.biClrUsed = 0;
716     info->bmiHeader.biClrImportant = 0;
717     info->bmiHeader.biBitCount = 32;
718     color_size = width * height * 4;
719     info->bmiHeader.biSizeImage = color_size;
720     hbmColor = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (VOID **) &color_bits, NULL, 0);
721     if (!hbmColor)
722     {
723         WARN("failed to create DIB section for cursor color data\n");
724         goto cleanup;
725     }
726     info->bmiHeader.biBitCount = 1;
727     info->bmiColors[0].rgbRed      = 0;
728     info->bmiColors[0].rgbGreen    = 0;
729     info->bmiColors[0].rgbBlue     = 0;
730     info->bmiColors[0].rgbReserved = 0;
731     info->bmiColors[1].rgbRed      = 0xff;
732     info->bmiColors[1].rgbGreen    = 0xff;
733     info->bmiColors[1].rgbBlue     = 0xff;
734     info->bmiColors[1].rgbReserved = 0;
735
736     mask_size = ((width + 31) / 32 * 4) * height; /* width_bytes * height */
737     info->bmiHeader.biSizeImage = mask_size;
738     hbmMask = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (VOID **) &mask_bits, NULL, 0);
739     if (!hbmMask)
740     {
741         WARN("failed to create DIB section for cursor mask data\n");
742         goto cleanup;
743     }
744
745     /* Create a CFDictionary for each frame of the cursor */
746     for (i = 0; i < nFrames; i++)
747     {
748         CFDictionaryRef frame = create_cursor_frame(hdc, iinfo, icon,
749                                                     hbmColor, color_bits, color_size,
750                                                     hbmMask, mask_bits, mask_size,
751                                                     width, height, i);
752         if (!frame) goto cleanup;
753         CFArrayAppendValue(frames, frame);
754         CFRelease(frame);
755     }
756
757 cleanup:
758     if (CFArrayGetCount(frames) < nFrames)
759     {
760         CFRelease(frames);
761         frames = NULL;
762     }
763     else
764         TRACE("returning cursor with %d frames\n", nFrames);
765     /* Cleanup all of the resources used to obtain the frame data */
766     if (hbmColor) DeleteObject(hbmColor);
767     if (hbmMask) DeleteObject(hbmMask);
768     HeapFree(GetProcessHeap(), 0, info);
769     return frames;
770 }
771
772
773 /***********************************************************************
774  *              DestroyCursorIcon (MACDRV.@)
775  */
776 void CDECL macdrv_DestroyCursorIcon(HCURSOR cursor)
777 {
778     TRACE("cursor %p\n", cursor);
779
780     EnterCriticalSection(&cursor_cache_section);
781     if (cursor_cache)
782         CFDictionaryRemoveValue(cursor_cache, cursor);
783     LeaveCriticalSection(&cursor_cache_section);
784 }
785
786
787 /***********************************************************************
788  *              ClipCursor (MACDRV.@)
789  *
790  * Set the cursor clipping rectangle.
791  */
792 BOOL CDECL macdrv_ClipCursor(LPCRECT clip)
793 {
794     CGRect rect;
795
796     TRACE("%s\n", wine_dbgstr_rect(clip));
797
798     if (clip)
799     {
800         rect = CGRectMake(clip->left, clip->top, max(1, clip->right - clip->left),
801                           max(1, clip->bottom - clip->top));
802     }
803     else
804         rect = CGRectInfinite;
805
806     /* FIXME: This needs to be done not just in this process but in all of the
807        ones for this WINEPREFIX.  Broadcast a message to do that. */
808
809     return macdrv_clip_cursor(rect);
810 }
811
812
813 /***********************************************************************
814  *              GetCursorPos (MACDRV.@)
815  */
816 BOOL CDECL macdrv_GetCursorPos(LPPOINT pos)
817 {
818     CGPoint pt;
819     BOOL ret;
820
821     ret = macdrv_get_cursor_position(&pt);
822     if (ret)
823     {
824         TRACE("pointer at (%g,%g) server pos %d,%d\n", pt.x, pt.y, pos->x, pos->y);
825         pos->x = pt.x;
826         pos->y = pt.y;
827     }
828     return ret;
829 }
830
831
832 /***********************************************************************
833  *              SetCursor (MACDRV.@)
834  */
835 void CDECL macdrv_SetCursor(HCURSOR cursor)
836 {
837     CFStringRef cursor_name = NULL;
838     CFArrayRef cursor_frames = NULL;
839
840     TRACE("%p\n", cursor);
841
842     if (cursor)
843     {
844         ICONINFOEXW info;
845
846         EnterCriticalSection(&cursor_cache_section);
847         if (cursor_cache)
848         {
849             CFTypeRef cached_cursor = CFDictionaryGetValue(cursor_cache, cursor);
850             if (cached_cursor)
851             {
852                 if (CFGetTypeID(cached_cursor) == CFStringGetTypeID())
853                     cursor_name = CFRetain(cached_cursor);
854                 else
855                     cursor_frames = CFRetain(cached_cursor);
856             }
857         }
858         LeaveCriticalSection(&cursor_cache_section);
859         if (cursor_name || cursor_frames)
860             goto done;
861
862         info.cbSize = sizeof(info);
863         if (!GetIconInfoExW(cursor, &info))
864         {
865             WARN("GetIconInfoExW failed\n");
866             return;
867         }
868
869         if ((cursor_name = copy_system_cursor_name(&info)))
870         {
871             DeleteObject(info.hbmColor);
872             DeleteObject(info.hbmMask);
873         }
874         else
875         {
876             BITMAP bm;
877             HDC hdc;
878
879             GetObjectW(info.hbmMask, sizeof(bm), &bm);
880             if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
881
882             /* make sure hotspot is valid */
883             if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
884             {
885                 info.xHotspot = bm.bmWidth / 2;
886                 info.yHotspot = bm.bmHeight / 2;
887             }
888
889             hdc = CreateCompatibleDC(0);
890
891             if (info.hbmColor)
892             {
893                 cursor_frames = create_color_cursor(hdc, &info, cursor, bm.bmWidth, bm.bmHeight);
894                 DeleteObject(info.hbmColor);
895             }
896             else
897                 cursor_frames = create_monochrome_cursor(hdc, &info, bm.bmWidth, bm.bmHeight);
898
899             DeleteObject(info.hbmMask);
900             DeleteDC(hdc);
901         }
902
903         if (cursor_name || cursor_frames)
904         {
905             EnterCriticalSection(&cursor_cache_section);
906             if (!cursor_cache)
907                 cursor_cache = CFDictionaryCreateMutable(NULL, 0, NULL,
908                                                          &kCFTypeDictionaryValueCallBacks);
909             CFDictionarySetValue(cursor_cache, cursor,
910                                  cursor_name ? (CFTypeRef)cursor_name : (CFTypeRef)cursor_frames);
911             LeaveCriticalSection(&cursor_cache_section);
912         }
913         else
914             cursor_name = CFRetain(CFSTR("arrowCursor"));
915     }
916
917 done:
918     TRACE("setting cursor with cursor_name %s cursor_frames %p\n", debugstr_cf(cursor_name), cursor_frames);
919     macdrv_set_cursor(cursor_name, cursor_frames);
920     if (cursor_name) CFRelease(cursor_name);
921     if (cursor_frames) CFRelease(cursor_frames);
922 }
923
924
925 /***********************************************************************
926  *              SetCursorPos (MACDRV.@)
927  */
928 BOOL CDECL macdrv_SetCursorPos(INT x, INT y)
929 {
930     BOOL ret = macdrv_set_cursor_position(CGPointMake(x, y));
931     if (ret)
932         TRACE("warped to %d,%d\n", x, y);
933     else
934         ERR("failed to warp to %d,%d\n", x, y);
935     return ret;
936 }
937
938
939 /***********************************************************************
940  *              macdrv_mouse_button
941  *
942  * Handler for MOUSE_BUTTON events.
943  */
944 void macdrv_mouse_button(HWND hwnd, const macdrv_event *event)
945 {
946     UINT flags = 0;
947     WORD data = 0;
948
949     TRACE("win %p button %d %s at (%d,%d) time %lu (%lu ticks ago)\n", hwnd, event->mouse_button.button,
950           (event->mouse_button.pressed ? "pressed" : "released"),
951           event->mouse_button.x, event->mouse_button.y,
952           event->mouse_button.time_ms, (GetTickCount() - event->mouse_button.time_ms));
953
954     if (event->mouse_button.pressed)
955     {
956         switch (event->mouse_button.button)
957         {
958         case 0: flags |= MOUSEEVENTF_LEFTDOWN; break;
959         case 1: flags |= MOUSEEVENTF_RIGHTDOWN; break;
960         case 2: flags |= MOUSEEVENTF_MIDDLEDOWN; break;
961         default:
962             flags |= MOUSEEVENTF_XDOWN;
963             data = 1 << (event->mouse_button.button - 3);
964             break;
965         }
966     }
967     else
968     {
969         switch (event->mouse_button.button)
970         {
971         case 0: flags |= MOUSEEVENTF_LEFTUP; break;
972         case 1: flags |= MOUSEEVENTF_RIGHTUP; break;
973         case 2: flags |= MOUSEEVENTF_MIDDLEUP; break;
974         default:
975             flags |= MOUSEEVENTF_XUP;
976             data = 1 << (event->mouse_button.button - 3);
977             break;
978         }
979     }
980
981     send_mouse_input(hwnd, flags | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
982                      event->mouse_button.x, event->mouse_button.y,
983                      data, event->mouse_button.time_ms);
984 }
985
986
987 /***********************************************************************
988  *              macdrv_mouse_moved
989  *
990  * Handler for MOUSE_MOVED and MOUSE_MOVED_ABSOLUTE events.
991  */
992 void macdrv_mouse_moved(HWND hwnd, const macdrv_event *event)
993 {
994     UINT flags = MOUSEEVENTF_MOVE;
995
996     TRACE("win %p/%p %s (%d,%d) time %lu (%lu ticks ago)\n", hwnd, event->window,
997           (event->type == MOUSE_MOVED) ? "relative" : "absolute",
998           event->mouse_moved.x, event->mouse_moved.y,
999           event->mouse_moved.time_ms, (GetTickCount() - event->mouse_moved.time_ms));
1000
1001     if (event->type == MOUSE_MOVED_ABSOLUTE)
1002         flags |= MOUSEEVENTF_ABSOLUTE;
1003
1004     send_mouse_input(hwnd, flags, event->mouse_moved.x, event->mouse_moved.y,
1005                      0, event->mouse_moved.time_ms);
1006 }
1007
1008
1009 /***********************************************************************
1010  *              macdrv_mouse_scroll
1011  *
1012  * Handler for MOUSE_SCROLL events.
1013  */
1014 void macdrv_mouse_scroll(HWND hwnd, const macdrv_event *event)
1015 {
1016     TRACE("win %p/%p scroll (%d,%d) at (%d,%d) time %lu (%lu ticks ago)\n", hwnd,
1017           event->window, event->mouse_scroll.x_scroll, event->mouse_scroll.y_scroll,
1018           event->mouse_scroll.x, event->mouse_scroll.y,
1019           event->mouse_scroll.time_ms, (GetTickCount() - event->mouse_scroll.time_ms));
1020
1021     send_mouse_input(hwnd, MOUSEEVENTF_WHEEL | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1022                      event->mouse_scroll.x, event->mouse_scroll.y,
1023                      event->mouse_scroll.y_scroll, event->mouse_scroll.time_ms);
1024     send_mouse_input(hwnd, MOUSEEVENTF_HWHEEL | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
1025                      event->mouse_scroll.x, event->mouse_scroll.y,
1026                      event->mouse_scroll.x_scroll, event->mouse_scroll.time_ms);
1027 }