winemac: Implement WGL_EXT_swap_control.
[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  *              release_provider_cfdata
262  *
263  * Helper for create_monochrome_cursor.  A CFData is used by two
264  * different CGDataProviders, using different offsets.  One of them is
265  * constructed with a pointer to the bytes, not a reference to the
266  * CFData object (because of the offset).  So, the CFData is CFRetain'ed
267  * on its behalf at creation and released here.
268  */
269 void release_provider_cfdata(void *info, const void *data, size_t size)
270 {
271     CFRelease(info);
272 }
273
274
275 /***********************************************************************
276  *              create_monochrome_cursor
277  */
278 CFArrayRef create_monochrome_cursor(HDC hdc, const ICONINFOEXW *icon, int width, int height)
279 {
280     char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
281     BITMAPINFO *info = (BITMAPINFO *)buffer;
282     unsigned int width_bytes = (width + 31) / 32 * 4;
283     CGColorSpaceRef colorspace;
284     CFMutableDataRef data;
285     CGDataProviderRef provider;
286     CGImageRef cgimage, cgmask, cgmasked;
287     CGPoint hot_spot;
288     CFDictionaryRef hot_spot_dict;
289     const CFStringRef keys[] = { CFSTR("image"), CFSTR("hotSpot") };
290     CFTypeRef values[sizeof(keys) / sizeof(keys[0])];
291     CFDictionaryRef frame;
292     CFArrayRef frames;
293
294     TRACE("hdc %p icon->hbmMask %p icon->xHotspot %d icon->yHotspot %d width %d height %d\n",
295           hdc, icon->hbmMask, icon->xHotspot, icon->yHotspot, width, height);
296
297     info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
298     info->bmiHeader.biWidth = width;
299     info->bmiHeader.biHeight = -height * 2;
300     info->bmiHeader.biPlanes = 1;
301     info->bmiHeader.biBitCount = 1;
302     info->bmiHeader.biCompression = BI_RGB;
303     info->bmiHeader.biSizeImage = width_bytes * height * 2;
304     info->bmiHeader.biXPelsPerMeter = 0;
305     info->bmiHeader.biYPelsPerMeter = 0;
306     info->bmiHeader.biClrUsed = 0;
307     info->bmiHeader.biClrImportant = 0;
308
309     /* This will be owned by the data provider for the mask image and released
310        when it is destroyed. */
311     data = CFDataCreateMutable(NULL, info->bmiHeader.biSizeImage);
312     if (!data)
313     {
314         WARN("failed to create data\n");
315         return NULL;
316     }
317     CFDataSetLength(data, info->bmiHeader.biSizeImage);
318
319     if (!GetDIBits(hdc, icon->hbmMask, 0, height * 2, CFDataGetMutableBytePtr(data), info, DIB_RGB_COLORS))
320     {
321         WARN("GetDIBits failed\n");
322         CFRelease(data);
323         return NULL;
324     }
325
326     colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
327     if (!colorspace)
328     {
329         WARN("failed to create colorspace\n");
330         CFRelease(data);
331         return NULL;
332     }
333
334     /* The data object needs to live as long as this provider, so retain it an
335        extra time and have the provider's data-release callback release it. */
336     provider = CGDataProviderCreateWithData(data, CFDataGetBytePtr(data) + width_bytes * height,
337                                             width_bytes * height, release_provider_cfdata);
338     if (!provider)
339     {
340         WARN("failed to create data provider\n");
341         CGColorSpaceRelease(colorspace);
342         CFRelease(data);
343         return NULL;
344     }
345     CFRetain(data);
346
347     cgimage = CGImageCreate(width, height, 1, 1, width_bytes, colorspace,
348                             kCGImageAlphaNone | kCGBitmapByteOrderDefault,
349                             provider, NULL, FALSE, kCGRenderingIntentDefault);
350     CGDataProviderRelease(provider);
351     CGColorSpaceRelease(colorspace);
352     if (!cgimage)
353     {
354         WARN("failed to create image\n");
355         CFRelease(data);
356         return NULL;
357     }
358
359     provider = CGDataProviderCreateWithCFData(data);
360     CFRelease(data);
361     if (!provider)
362     {
363         WARN("failed to create data provider\n");
364         CGImageRelease(cgimage);
365         return NULL;
366     }
367
368     cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
369     CGDataProviderRelease(provider);
370     if (!cgmask)
371     {
372         WARN("failed to create mask image\n");
373         CGImageRelease(cgimage);
374         return NULL;
375     }
376
377     cgmasked = CGImageCreateWithMask(cgimage, cgmask);
378     CGImageRelease(cgimage);
379     CGImageRelease(cgmask);
380     if (!cgmasked)
381     {
382         WARN("failed to create masked image\n");
383         return NULL;
384     }
385
386     hot_spot = CGPointMake(icon->xHotspot, icon->yHotspot);
387     hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
388     if (!hot_spot_dict)
389     {
390         WARN("failed to create hot spot dictionary\n");
391         CGImageRelease(cgmasked);
392         return NULL;
393     }
394
395     values[0] = cgmasked;
396     values[1] = hot_spot_dict;
397     frame = CFDictionaryCreate(NULL, (const void**)keys, values, sizeof(keys) / sizeof(keys[0]),
398                                &kCFCopyStringDictionaryKeyCallBacks,
399                                &kCFTypeDictionaryValueCallBacks);
400     CFRelease(hot_spot_dict);
401     CGImageRelease(cgmasked);
402     if (!frame)
403     {
404         WARN("failed to create frame dictionary\n");
405         return NULL;
406     }
407
408     frames = CFArrayCreate(NULL, (const void**)&frame, 1, &kCFTypeArrayCallBacks);
409     CFRelease(frame);
410     if (!frames)
411     {
412         WARN("failed to create frames array\n");
413         return NULL;
414     }
415
416     return frames;
417 }
418
419
420 /***********************************************************************
421  *              create_cgimage_from_icon
422  */
423 static CGImageRef create_cgimage_from_icon(HDC hdc, HANDLE icon, HBITMAP hbmColor,
424                                            unsigned char *color_bits, int color_size, HBITMAP hbmMask,
425                                            unsigned char *mask_bits, int mask_size, int width,
426                                            int height, int istep)
427 {
428     int i, has_alpha = FALSE;
429     DWORD *ptr;
430     CGBitmapInfo alpha_format;
431     CGColorSpaceRef colorspace;
432     CFDataRef data;
433     CGDataProviderRef provider;
434     CGImageRef cgimage;
435
436     /* draw the cursor frame to a temporary buffer then create a CGImage from that */
437     memset(color_bits, 0x00, color_size);
438     SelectObject(hdc, hbmColor);
439     if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_NORMAL))
440     {
441         WARN("Could not draw frame %d (walk past end of frames).\n", istep);
442         return NULL;
443     }
444
445     /* check if the cursor frame was drawn with an alpha channel */
446     for (i = 0, ptr = (DWORD*)color_bits; i < width * height; i++, ptr++)
447         if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
448
449     if (has_alpha)
450         alpha_format = kCGImageAlphaFirst;
451     else
452         alpha_format = kCGImageAlphaNoneSkipFirst;
453
454     colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
455     if (!colorspace)
456     {
457         WARN("failed to create colorspace\n");
458         return NULL;
459     }
460
461     data = CFDataCreate(NULL, (UInt8*)color_bits, color_size);
462     if (!data)
463     {
464         WARN("failed to create data\n");
465         CGColorSpaceRelease(colorspace);
466         return NULL;
467     }
468
469     provider = CGDataProviderCreateWithCFData(data);
470     CFRelease(data);
471     if (!provider)
472     {
473         WARN("failed to create data provider\n");
474         CGColorSpaceRelease(colorspace);
475         return NULL;
476     }
477
478     cgimage = CGImageCreate(width, height, 8, 32, width * 4, colorspace,
479                             alpha_format | kCGBitmapByteOrder32Little,
480                             provider, NULL, FALSE, kCGRenderingIntentDefault);
481     CGDataProviderRelease(provider);
482     CGColorSpaceRelease(colorspace);
483     if (!cgimage)
484     {
485         WARN("failed to create image\n");
486         return NULL;
487     }
488
489     /* if no alpha channel was drawn then generate it from the mask */
490     if (!has_alpha)
491     {
492         unsigned int width_bytes = (width + 31) / 32 * 4;
493         CGImageRef cgmask, temp;
494
495         /* draw the cursor mask to a temporary buffer */
496         memset(mask_bits, 0xFF, mask_size);
497         SelectObject(hdc, hbmMask);
498         if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_MASK))
499         {
500             WARN("Failed to draw frame mask %d.\n", istep);
501             CGImageRelease(cgimage);
502             return NULL;
503         }
504
505         data = CFDataCreate(NULL, (UInt8*)mask_bits, mask_size);
506         if (!data)
507         {
508             WARN("failed to create data\n");
509             CGImageRelease(cgimage);
510             return NULL;
511         }
512
513         provider = CGDataProviderCreateWithCFData(data);
514         CFRelease(data);
515         if (!provider)
516         {
517             WARN("failed to create data provider\n");
518             CGImageRelease(cgimage);
519             return NULL;
520         }
521
522         cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
523         CGDataProviderRelease(provider);
524         if (!cgmask)
525         {
526             WARN("failed to create mask\n");
527             CGImageRelease(cgimage);
528             return NULL;
529         }
530
531         temp = CGImageCreateWithMask(cgimage, cgmask);
532         CGImageRelease(cgmask);
533         CGImageRelease(cgimage);
534         if (!temp)
535         {
536             WARN("failed to create masked image\n");
537             return NULL;
538         }
539         cgimage = temp;
540     }
541
542     return cgimage;
543 }
544
545
546 /***********************************************************************
547  *              create_cursor_frame
548  *
549  * Create a frame dictionary for a cursor from a Windows icon.
550  * Keys:
551  *      "image"     a CGImage for the frame
552  *      "duration"  a CFNumber for the frame duration in seconds
553  *      "hotSpot"   a CFDictionary encoding a CGPoint for the hot spot
554  */
555 static CFDictionaryRef create_cursor_frame(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon,
556                                            HBITMAP hbmColor, unsigned char *color_bits, int color_size,
557                                            HBITMAP hbmMask, unsigned char *mask_bits, int mask_size,
558                                            int width, int height, int istep)
559 {
560     DWORD delay_jiffies, num_steps;
561     CFMutableDictionaryRef frame;
562     CGPoint hot_spot;
563     CFDictionaryRef hot_spot_dict;
564     double duration;
565     CFNumberRef duration_number;
566     CGImageRef cgimage;
567
568     TRACE("hdc %p iinfo->xHotspot %d iinfo->yHotspot %d icon %p hbmColor %p color_bits %p color_size %d"
569           " hbmMask %p mask_bits %p mask_size %d width %d height %d istep %d\n",
570           hdc, iinfo->xHotspot, iinfo->yHotspot, icon, hbmColor, color_bits, color_size,
571           hbmMask, mask_bits, mask_size, width, height, istep);
572
573     frame = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks,
574                                       &kCFTypeDictionaryValueCallBacks);
575     if (!frame)
576     {
577         WARN("failed to allocate dictionary for frame\n");
578         return NULL;
579     }
580
581     hot_spot = CGPointMake(iinfo->xHotspot, iinfo->yHotspot);
582     hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
583     if (!hot_spot_dict)
584     {
585         WARN("failed to create hot spot dictionary\n");
586         CFRelease(frame);
587         return NULL;
588     }
589     CFDictionarySetValue(frame, CFSTR("hotSpot"), hot_spot_dict);
590     CFRelease(hot_spot_dict);
591
592     if (GetCursorFrameInfo(icon, 0x0 /* unknown parameter */, istep, &delay_jiffies, &num_steps) != 0)
593         duration = delay_jiffies / 60.0; /* convert jiffies (1/60s) to seconds */
594     else
595     {
596         WARN("Failed to retrieve animated cursor frame-rate for frame %d.\n", istep);
597         duration = 0.1; /* fallback delay, 100 ms */
598     }
599     duration_number = CFNumberCreate(NULL, kCFNumberDoubleType, &duration);
600     if (!duration_number)
601     {
602         WARN("failed to create duration number\n");
603         CFRelease(frame);
604         return NULL;
605     }
606     CFDictionarySetValue(frame, CFSTR("duration"), duration_number);
607     CFRelease(duration_number);
608
609     cgimage = create_cgimage_from_icon(hdc, icon, hbmColor, color_bits, color_size,
610                                        hbmMask, mask_bits, mask_size, width, height, istep);
611     if (!cgimage)
612     {
613         CFRelease(frame);
614         return NULL;
615     }
616
617     CFDictionarySetValue(frame, CFSTR("image"), cgimage);
618     CGImageRelease(cgimage);
619
620     return frame;
621 }
622
623
624 /***********************************************************************
625  *              create_color_cursor
626  *
627  * Create an array of color cursor frames from a Windows cursor.  Each
628  * frame is represented in the array by a dictionary.
629  * Frame dictionary keys:
630  *      "image"     a CGImage for the frame
631  *      "duration"  a CFNumber for the frame duration in seconds
632  *      "hotSpot"   a CFDictionary encoding a CGPoint for the hot spot
633  */
634 static CFArrayRef create_color_cursor(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon, int width, int height)
635 {
636     unsigned char *color_bits, *mask_bits;
637     HBITMAP hbmColor = 0, hbmMask = 0;
638     DWORD nFrames, delay_jiffies, i;
639     int color_size, mask_size;
640     BITMAPINFO *info = NULL;
641     CFMutableArrayRef frames;
642
643     TRACE("hdc %p iinfo %p icon %p width %d height %d\n", hdc, iinfo, icon, width, height);
644
645     /* Retrieve the number of frames to render */
646     if (!GetCursorFrameInfo(icon, 0x0 /* unknown parameter */, 0, &delay_jiffies, &nFrames))
647     {
648         WARN("GetCursorFrameInfo failed\n");
649         return NULL;
650     }
651     if (!(frames = CFArrayCreateMutable(NULL, nFrames, &kCFTypeArrayCallBacks)))
652     {
653         WARN("failed to allocate frames array\n");
654         return NULL;
655     }
656
657     /* Allocate all of the resources necessary to obtain a cursor frame */
658     if (!(info = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(BITMAPINFO, bmiColors[256])))) goto cleanup;
659     info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
660     info->bmiHeader.biWidth = width;
661     info->bmiHeader.biHeight = -height;
662     info->bmiHeader.biPlanes = 1;
663     info->bmiHeader.biCompression = BI_RGB;
664     info->bmiHeader.biXPelsPerMeter = 0;
665     info->bmiHeader.biYPelsPerMeter = 0;
666     info->bmiHeader.biClrUsed = 0;
667     info->bmiHeader.biClrImportant = 0;
668     info->bmiHeader.biBitCount = 32;
669     color_size = width * height * 4;
670     info->bmiHeader.biSizeImage = color_size;
671     hbmColor = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (VOID **) &color_bits, NULL, 0);
672     if (!hbmColor)
673     {
674         WARN("failed to create DIB section for cursor color data\n");
675         goto cleanup;
676     }
677     info->bmiHeader.biBitCount = 1;
678     info->bmiColors[0].rgbRed      = 0;
679     info->bmiColors[0].rgbGreen    = 0;
680     info->bmiColors[0].rgbBlue     = 0;
681     info->bmiColors[0].rgbReserved = 0;
682     info->bmiColors[1].rgbRed      = 0xff;
683     info->bmiColors[1].rgbGreen    = 0xff;
684     info->bmiColors[1].rgbBlue     = 0xff;
685     info->bmiColors[1].rgbReserved = 0;
686
687     mask_size = ((width + 31) / 32 * 4) * height; /* width_bytes * height */
688     info->bmiHeader.biSizeImage = mask_size;
689     hbmMask = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (VOID **) &mask_bits, NULL, 0);
690     if (!hbmMask)
691     {
692         WARN("failed to create DIB section for cursor mask data\n");
693         goto cleanup;
694     }
695
696     /* Create a CFDictionary for each frame of the cursor */
697     for (i = 0; i < nFrames; i++)
698     {
699         CFDictionaryRef frame = create_cursor_frame(hdc, iinfo, icon,
700                                                     hbmColor, color_bits, color_size,
701                                                     hbmMask, mask_bits, mask_size,
702                                                     width, height, i);
703         if (!frame) goto cleanup;
704         CFArrayAppendValue(frames, frame);
705         CFRelease(frame);
706     }
707
708 cleanup:
709     if (CFArrayGetCount(frames) < nFrames)
710     {
711         CFRelease(frames);
712         frames = NULL;
713     }
714     else
715         TRACE("returning cursor with %d frames\n", nFrames);
716     /* Cleanup all of the resources used to obtain the frame data */
717     if (hbmColor) DeleteObject(hbmColor);
718     if (hbmMask) DeleteObject(hbmMask);
719     HeapFree(GetProcessHeap(), 0, info);
720     return frames;
721 }
722
723
724 /***********************************************************************
725  *              DestroyCursorIcon (MACDRV.@)
726  */
727 void CDECL macdrv_DestroyCursorIcon(HCURSOR cursor)
728 {
729     TRACE("cursor %p\n", cursor);
730
731     EnterCriticalSection(&cursor_cache_section);
732     if (cursor_cache)
733         CFDictionaryRemoveValue(cursor_cache, cursor);
734     LeaveCriticalSection(&cursor_cache_section);
735 }
736
737
738 /***********************************************************************
739  *              ClipCursor (MACDRV.@)
740  *
741  * Set the cursor clipping rectangle.
742  */
743 BOOL CDECL macdrv_ClipCursor(LPCRECT clip)
744 {
745     CGRect rect;
746
747     TRACE("%s\n", wine_dbgstr_rect(clip));
748
749     if (clip)
750     {
751         rect = CGRectMake(clip->left, clip->top, max(1, clip->right - clip->left),
752                           max(1, clip->bottom - clip->top));
753     }
754     else
755         rect = CGRectInfinite;
756
757     /* FIXME: This needs to be done not just in this process but in all of the
758        ones for this WINEPREFIX.  Broadcast a message to do that. */
759
760     return macdrv_clip_cursor(rect);
761 }
762
763
764 /***********************************************************************
765  *              GetCursorPos (MACDRV.@)
766  */
767 BOOL CDECL macdrv_GetCursorPos(LPPOINT pos)
768 {
769     CGPoint pt;
770     BOOL ret;
771
772     ret = macdrv_get_cursor_position(&pt);
773     if (ret)
774     {
775         TRACE("pointer at (%g,%g) server pos %d,%d\n", pt.x, pt.y, pos->x, pos->y);
776         pos->x = pt.x;
777         pos->y = pt.y;
778     }
779     return ret;
780 }
781
782
783 /***********************************************************************
784  *              SetCursor (MACDRV.@)
785  */
786 void CDECL macdrv_SetCursor(HCURSOR cursor)
787 {
788     CFStringRef cursor_name = NULL;
789     CFArrayRef cursor_frames = NULL;
790
791     TRACE("%p\n", cursor);
792
793     if (cursor)
794     {
795         ICONINFOEXW info;
796
797         EnterCriticalSection(&cursor_cache_section);
798         if (cursor_cache)
799         {
800             CFTypeRef cached_cursor = CFDictionaryGetValue(cursor_cache, cursor);
801             if (cached_cursor)
802             {
803                 if (CFGetTypeID(cached_cursor) == CFStringGetTypeID())
804                     cursor_name = CFRetain(cached_cursor);
805                 else
806                     cursor_frames = CFRetain(cached_cursor);
807             }
808         }
809         LeaveCriticalSection(&cursor_cache_section);
810         if (cursor_name || cursor_frames)
811             goto done;
812
813         info.cbSize = sizeof(info);
814         if (!GetIconInfoExW(cursor, &info))
815         {
816             WARN("GetIconInfoExW failed\n");
817             return;
818         }
819
820         if ((cursor_name = copy_system_cursor_name(&info)))
821         {
822             DeleteObject(info.hbmColor);
823             DeleteObject(info.hbmMask);
824         }
825         else
826         {
827             BITMAP bm;
828             HDC hdc;
829
830             GetObjectW(info.hbmMask, sizeof(bm), &bm);
831             if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
832
833             /* make sure hotspot is valid */
834             if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
835             {
836                 info.xHotspot = bm.bmWidth / 2;
837                 info.yHotspot = bm.bmHeight / 2;
838             }
839
840             hdc = CreateCompatibleDC(0);
841
842             if (info.hbmColor)
843             {
844                 cursor_frames = create_color_cursor(hdc, &info, cursor, bm.bmWidth, bm.bmHeight);
845                 DeleteObject(info.hbmColor);
846             }
847             else
848                 cursor_frames = create_monochrome_cursor(hdc, &info, bm.bmWidth, bm.bmHeight);
849
850             DeleteObject(info.hbmMask);
851             DeleteDC(hdc);
852         }
853
854         if (cursor_name || cursor_frames)
855         {
856             EnterCriticalSection(&cursor_cache_section);
857             if (!cursor_cache)
858                 cursor_cache = CFDictionaryCreateMutable(NULL, 0, NULL,
859                                                          &kCFTypeDictionaryValueCallBacks);
860             CFDictionarySetValue(cursor_cache, cursor,
861                                  cursor_name ? (CFTypeRef)cursor_name : (CFTypeRef)cursor_frames);
862             LeaveCriticalSection(&cursor_cache_section);
863         }
864         else
865             cursor_name = CFRetain(CFSTR("arrowCursor"));
866     }
867
868 done:
869     TRACE("setting cursor with cursor_name %s cursor_frames %p\n", debugstr_cf(cursor_name), cursor_frames);
870     macdrv_set_cursor(cursor_name, cursor_frames);
871     if (cursor_name) CFRelease(cursor_name);
872     if (cursor_frames) CFRelease(cursor_frames);
873 }
874
875
876 /***********************************************************************
877  *              SetCursorPos (MACDRV.@)
878  */
879 BOOL CDECL macdrv_SetCursorPos(INT x, INT y)
880 {
881     BOOL ret = macdrv_set_cursor_position(CGPointMake(x, y));
882     if (ret)
883         TRACE("warped to %d,%d\n", x, y);
884     else
885         ERR("failed to warp to %d,%d\n", x, y);
886     return ret;
887 }
888
889
890 /***********************************************************************
891  *              macdrv_mouse_button
892  *
893  * Handler for MOUSE_BUTTON events.
894  */
895 void macdrv_mouse_button(HWND hwnd, const macdrv_event *event)
896 {
897     UINT flags = 0;
898     WORD data = 0;
899
900     TRACE("win %p button %d %s at (%d,%d) time %lu (%lu ticks ago)\n", hwnd, event->mouse_button.button,
901           (event->mouse_button.pressed ? "pressed" : "released"),
902           event->mouse_button.x, event->mouse_button.y,
903           event->mouse_button.time_ms, (GetTickCount() - event->mouse_button.time_ms));
904
905     if (event->mouse_button.pressed)
906     {
907         switch (event->mouse_button.button)
908         {
909         case 0: flags |= MOUSEEVENTF_LEFTDOWN; break;
910         case 1: flags |= MOUSEEVENTF_RIGHTDOWN; break;
911         case 2: flags |= MOUSEEVENTF_MIDDLEDOWN; break;
912         default:
913             flags |= MOUSEEVENTF_XDOWN;
914             data = 1 << (event->mouse_button.button - 3);
915             break;
916         }
917     }
918     else
919     {
920         switch (event->mouse_button.button)
921         {
922         case 0: flags |= MOUSEEVENTF_LEFTUP; break;
923         case 1: flags |= MOUSEEVENTF_RIGHTUP; break;
924         case 2: flags |= MOUSEEVENTF_MIDDLEUP; break;
925         default:
926             flags |= MOUSEEVENTF_XUP;
927             data = 1 << (event->mouse_button.button - 3);
928             break;
929         }
930     }
931
932     send_mouse_input(hwnd, flags | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
933                      event->mouse_button.x, event->mouse_button.y,
934                      data, event->mouse_button.time_ms);
935 }
936
937
938 /***********************************************************************
939  *              macdrv_mouse_moved
940  *
941  * Handler for MOUSE_MOVED and MOUSE_MOVED_ABSOLUTE events.
942  */
943 void macdrv_mouse_moved(HWND hwnd, const macdrv_event *event)
944 {
945     UINT flags = MOUSEEVENTF_MOVE;
946
947     TRACE("win %p/%p %s (%d,%d) time %lu (%lu ticks ago)\n", hwnd, event->window,
948           (event->type == MOUSE_MOVED) ? "relative" : "absolute",
949           event->mouse_moved.x, event->mouse_moved.y,
950           event->mouse_moved.time_ms, (GetTickCount() - event->mouse_moved.time_ms));
951
952     if (event->type == MOUSE_MOVED_ABSOLUTE)
953         flags |= MOUSEEVENTF_ABSOLUTE;
954
955     send_mouse_input(hwnd, flags, event->mouse_moved.x, event->mouse_moved.y,
956                      0, event->mouse_moved.time_ms);
957 }
958
959
960 /***********************************************************************
961  *              macdrv_mouse_scroll
962  *
963  * Handler for MOUSE_SCROLL events.
964  */
965 void macdrv_mouse_scroll(HWND hwnd, const macdrv_event *event)
966 {
967     TRACE("win %p/%p scroll (%d,%d) at (%d,%d) time %lu (%lu ticks ago)\n", hwnd,
968           event->window, event->mouse_scroll.x_scroll, event->mouse_scroll.y_scroll,
969           event->mouse_scroll.x, event->mouse_scroll.y,
970           event->mouse_scroll.time_ms, (GetTickCount() - event->mouse_scroll.time_ms));
971
972     send_mouse_input(hwnd, MOUSEEVENTF_WHEEL | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
973                      event->mouse_scroll.x, event->mouse_scroll.y,
974                      event->mouse_scroll.y_scroll, event->mouse_scroll.time_ms);
975     send_mouse_input(hwnd, MOUSEEVENTF_HWHEEL | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
976                      event->mouse_scroll.x, event->mouse_scroll.y,
977                      event->mouse_scroll.x_scroll, event->mouse_scroll.time_ms);
978 }