Commit a49df4b3 authored by Ken Thomases's avatar Ken Thomases Committed by Alexandre Julliard

winemac: Implement SetCursor() and DestroyCursorIcon().

parent 1896e557
...@@ -50,6 +50,11 @@ ...@@ -50,6 +50,11 @@
NSMutableArray* orderedWineWindows; NSMutableArray* orderedWineWindows;
NSMutableDictionary* originalDisplayModes; NSMutableDictionary* originalDisplayModes;
NSArray* cursorFrames;
int cursorFrame;
NSTimer* cursorTimer;
BOOL cursorHidden;
} }
@property (nonatomic) CGEventSourceKeyboardType keyboardType; @property (nonatomic) CGEventSourceKeyboardType keyboardType;
......
...@@ -31,6 +31,8 @@ int macdrv_err_on; ...@@ -31,6 +31,8 @@ int macdrv_err_on;
@interface WineApplication () @interface WineApplication ()
@property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged; @property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
@property (copy, nonatomic) NSArray* cursorFrames;
@property (retain, nonatomic) NSTimer* cursorTimer;
@end @end
...@@ -39,6 +41,7 @@ int macdrv_err_on; ...@@ -39,6 +41,7 @@ int macdrv_err_on;
@synthesize keyboardType, lastFlagsChanged; @synthesize keyboardType, lastFlagsChanged;
@synthesize orderedWineWindows; @synthesize orderedWineWindows;
@synthesize cursorFrames, cursorTimer;
- (id) init - (id) init
{ {
...@@ -65,6 +68,8 @@ int macdrv_err_on; ...@@ -65,6 +68,8 @@ int macdrv_err_on;
- (void) dealloc - (void) dealloc
{ {
[cursorTimer release];
[cursorFrames release];
[originalDisplayModes release]; [originalDisplayModes release];
[orderedWineWindows release]; [orderedWineWindows release];
[keyWindows release]; [keyWindows release];
...@@ -460,6 +465,89 @@ int macdrv_err_on; ...@@ -460,6 +465,89 @@ int macdrv_err_on;
return ([originalDisplayModes count] > 0); return ([originalDisplayModes count] > 0);
} }
- (void) hideCursor
{
if (!cursorHidden)
{
[NSCursor hide];
cursorHidden = TRUE;
}
}
- (void) unhideCursor
{
if (cursorHidden)
{
[NSCursor unhide];
cursorHidden = FALSE;
}
}
- (void) setCursor
{
NSDictionary* frame = [cursorFrames objectAtIndex:cursorFrame];
CGImageRef cgimage = (CGImageRef)[frame objectForKey:@"image"];
NSImage* image = [[NSImage alloc] initWithCGImage:cgimage size:NSZeroSize];
CFDictionaryRef hotSpotDict = (CFDictionaryRef)[frame objectForKey:@"hotSpot"];
CGPoint hotSpot;
NSCursor* cursor;
if (!CGPointMakeWithDictionaryRepresentation(hotSpotDict, &hotSpot))
hotSpot = CGPointZero;
cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSPointFromCGPoint(hotSpot)];
[image release];
[cursor set];
[self unhideCursor];
[cursor release];
}
- (void) nextCursorFrame:(NSTimer*)theTimer
{
NSDictionary* frame;
NSTimeInterval duration;
NSDate* date;
cursorFrame++;
if (cursorFrame >= [cursorFrames count])
cursorFrame = 0;
[self setCursor];
frame = [cursorFrames objectAtIndex:cursorFrame];
duration = [[frame objectForKey:@"duration"] doubleValue];
date = [[theTimer fireDate] dateByAddingTimeInterval:duration];
[cursorTimer setFireDate:date];
}
- (void) setCursorWithFrames:(NSArray*)frames
{
if (self.cursorFrames == frames)
return;
self.cursorFrames = frames;
cursorFrame = 0;
[cursorTimer invalidate];
self.cursorTimer = nil;
if ([frames count])
{
if ([frames count] > 1)
{
NSDictionary* frame = [frames objectAtIndex:0];
NSTimeInterval duration = [[frame objectForKey:@"duration"] doubleValue];
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:duration];
self.cursorTimer = [[[NSTimer alloc] initWithFireDate:date
interval:1000000
target:self
selector:@selector(nextCursorFrame:)
userInfo:nil
repeats:YES] autorelease];
[[NSRunLoop currentRunLoop] addTimer:cursorTimer forMode:NSRunLoopCommonModes];
}
[self setCursor];
}
}
/* /*
* ---------- NSApplication method overrides ---------- * ---------- NSApplication method overrides ----------
...@@ -673,3 +761,57 @@ int macdrv_set_display_mode(const struct macdrv_display* display, ...@@ -673,3 +761,57 @@ int macdrv_set_display_mode(const struct macdrv_display* display,
return ret; return ret;
} }
/***********************************************************************
* macdrv_set_cursor
*
* Set the cursor.
*
* If name is non-NULL, it is a selector for a class method on NSCursor
* identifying the cursor to set. In that case, frames is ignored. If
* name is NULL, then frames is used.
*
* frames is an array of dictionaries. Each dictionary is a frame of
* an animated cursor. Under the key "image" is a CGImage for the
* frame. Under the key "duration" is a CFNumber time interval, in
* seconds, for how long that frame is presented before proceeding to
* the next frame. Under the key "hotSpot" is a CFDictionary encoding a
* CGPoint, to be decoded using CGPointMakeWithDictionaryRepresentation().
* This is the hot spot, measured in pixels down and to the right of the
* top-left corner of the image.
*
* If the array has exactly 1 element, the cursor is static, not
* animated. If frames is NULL or has 0 elements, the cursor is hidden.
*/
void macdrv_set_cursor(CFStringRef name, CFArrayRef frames)
{
SEL sel;
sel = NSSelectorFromString((NSString*)name);
if (sel)
{
OnMainThreadAsync(^{
NSCursor* cursor = [NSCursor performSelector:sel];
[NSApp setCursorWithFrames:nil];
[cursor set];
[NSApp unhideCursor];
});
}
else
{
NSArray* nsframes = (NSArray*)frames;
if ([nsframes count])
{
OnMainThreadAsync(^{
[NSApp setCursorWithFrames:nsframes];
});
}
else
{
OnMainThreadAsync(^{
[NSApp setCursorWithFrames:nil];
[NSApp hideCursor];
});
}
}
}
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#include "wine/gdi_driver.h" #include "wine/gdi_driver.h"
extern const char* debugstr_cf(CFTypeRef t) DECLSPEC_HIDDEN;
static inline CGRect cgrect_from_rect(RECT rect) static inline CGRect cgrect_from_rect(RECT rect)
{ {
if (rect.left >= rect.right || rect.top >= rect.bottom) if (rect.left >= rect.right || rect.top >= rect.bottom)
......
...@@ -118,6 +118,10 @@ extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLS ...@@ -118,6 +118,10 @@ extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLS
extern void macdrv_beep(void) DECLSPEC_HIDDEN; extern void macdrv_beep(void) DECLSPEC_HIDDEN;
/* cursor */
extern void macdrv_set_cursor(CFStringRef name, CFArrayRef frames) DECLSPEC_HIDDEN;
/* display */ /* display */
extern int macdrv_get_displays(struct macdrv_display** displays, int* count) DECLSPEC_HIDDEN; extern int macdrv_get_displays(struct macdrv_display** displays, int* count) DECLSPEC_HIDDEN;
extern void macdrv_free_displays(struct macdrv_display* displays) DECLSPEC_HIDDEN; extern void macdrv_free_displays(struct macdrv_display* displays) DECLSPEC_HIDDEN;
......
...@@ -30,6 +30,40 @@ WINE_DEFAULT_DEBUG_CHANNEL(macdrv); ...@@ -30,6 +30,40 @@ WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
DWORD thread_data_tls_index = TLS_OUT_OF_INDEXES; DWORD thread_data_tls_index = TLS_OUT_OF_INDEXES;
/**************************************************************************
* debugstr_cf
*/
const char* debugstr_cf(CFTypeRef t)
{
CFStringRef s;
const char* ret;
if (!t) return "(null)";
if (CFGetTypeID(t) == CFStringGetTypeID())
s = t;
else
s = CFCopyDescription(t);
ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
if (ret) ret = debugstr_a(ret);
if (!ret)
{
const UniChar* u = CFStringGetCharactersPtr(s);
if (u)
ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s));
}
if (!ret)
{
UniChar buf[200];
int len = min(CFStringGetLength(s), sizeof(buf)/sizeof(buf[0]));
CFStringGetCharacters(s, CFRangeMake(0, len), buf);
ret = debugstr_wn(buf, len);
}
if (s != t) CFRelease(s);
return ret;
}
/*********************************************************************** /***********************************************************************
* process_attach * process_attach
*/ */
......
...@@ -23,12 +23,27 @@ ...@@ -23,12 +23,27 @@
#include "config.h" #include "config.h"
#include "macdrv.h" #include "macdrv.h"
#define OEMRESOURCE
#include "winuser.h" #include "winuser.h"
#include "winreg.h"
#include "wine/server.h" #include "wine/server.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(cursor); WINE_DEFAULT_DEBUG_CHANNEL(cursor);
static CRITICAL_SECTION cursor_cache_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &cursor_cache_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": cursor_cache_section") }
};
static CRITICAL_SECTION cursor_cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
static CFMutableDictionaryRef cursor_cache;
/*********************************************************************** /***********************************************************************
* send_mouse_input * send_mouse_input
* *
...@@ -75,6 +90,568 @@ static void send_mouse_input(HWND hwnd, UINT flags, int x, int y, ...@@ -75,6 +90,568 @@ static void send_mouse_input(HWND hwnd, UINT flags, int x, int y,
/*********************************************************************** /***********************************************************************
* release_provider_cfdata
*
* Helper for create_monochrome_cursor. A CFData is used by two
* different CGDataProviders, using different offsets. One of them is
* constructed with a pointer to the bytes, not a reference to the
* CFData object (because of the offset). So, the CFData is CFRetain'ed
* on its behalf at creation and released here.
*/
void release_provider_cfdata(void *info, const void *data, size_t size)
{
CFRelease(info);
}
/***********************************************************************
* create_monochrome_cursor
*/
CFArrayRef create_monochrome_cursor(HDC hdc, const ICONINFOEXW *icon, int width, int height)
{
char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
BITMAPINFO *info = (BITMAPINFO *)buffer;
unsigned int width_bytes = (width + 31) / 32 * 4;
CGColorSpaceRef colorspace;
CFMutableDataRef data;
CGDataProviderRef provider;
CGImageRef cgimage, cgmask, cgmasked;
CGPoint hot_spot;
CFDictionaryRef hot_spot_dict;
const CFStringRef keys[] = { CFSTR("image"), CFSTR("hotSpot") };
CFTypeRef values[sizeof(keys) / sizeof(keys[0])];
CFDictionaryRef frame;
CFArrayRef frames;
TRACE("hdc %p icon->hbmMask %p icon->xHotspot %d icon->yHotspot %d width %d height %d\n",
hdc, icon->hbmMask, icon->xHotspot, icon->yHotspot, width, height);
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biWidth = width;
info->bmiHeader.biHeight = -height * 2;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 1;
info->bmiHeader.biCompression = BI_RGB;
info->bmiHeader.biSizeImage = width_bytes * height * 2;
info->bmiHeader.biXPelsPerMeter = 0;
info->bmiHeader.biYPelsPerMeter = 0;
info->bmiHeader.biClrUsed = 0;
info->bmiHeader.biClrImportant = 0;
/* This will be owned by the data provider for the mask image and released
when it is destroyed. */
data = CFDataCreateMutable(NULL, info->bmiHeader.biSizeImage);
if (!data)
{
WARN("failed to create data\n");
return NULL;
}
CFDataSetLength(data, info->bmiHeader.biSizeImage);
if (!GetDIBits(hdc, icon->hbmMask, 0, height * 2, CFDataGetMutableBytePtr(data), info, DIB_RGB_COLORS))
{
WARN("GetDIBits failed\n");
CFRelease(data);
return NULL;
}
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
if (!colorspace)
{
WARN("failed to create colorspace\n");
CFRelease(data);
return NULL;
}
/* The data object needs to live as long as this provider, so retain it an
extra time and have the provider's data-release callback release it. */
provider = CGDataProviderCreateWithData(data, CFDataGetBytePtr(data) + width_bytes * height,
width_bytes * height, release_provider_cfdata);
if (!provider)
{
WARN("failed to create data provider\n");
CGColorSpaceRelease(colorspace);
CFRelease(data);
return NULL;
}
CFRetain(data);
cgimage = CGImageCreate(width, height, 1, 1, width_bytes, colorspace,
kCGImageAlphaNone | kCGBitmapByteOrderDefault,
provider, NULL, FALSE, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorspace);
if (!cgimage)
{
WARN("failed to create image\n");
CFRelease(data);
return NULL;
}
provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
if (!provider)
{
WARN("failed to create data provider\n");
CGImageRelease(cgimage);
return NULL;
}
cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
CGDataProviderRelease(provider);
if (!cgmask)
{
WARN("failed to create mask image\n");
CGImageRelease(cgimage);
return NULL;
}
cgmasked = CGImageCreateWithMask(cgimage, cgmask);
CGImageRelease(cgimage);
CGImageRelease(cgmask);
if (!cgmasked)
{
WARN("failed to create masked image\n");
return NULL;
}
hot_spot = CGPointMake(icon->xHotspot, icon->yHotspot);
hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
if (!hot_spot_dict)
{
WARN("failed to create hot spot dictionary\n");
CGImageRelease(cgmasked);
return NULL;
}
values[0] = cgmasked;
values[1] = hot_spot_dict;
frame = CFDictionaryCreate(NULL, (const void**)keys, values, sizeof(keys) / sizeof(keys[0]),
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(hot_spot_dict);
CGImageRelease(cgmasked);
if (!frame)
{
WARN("failed to create frame dictionary\n");
return NULL;
}
frames = CFArrayCreate(NULL, (const void**)&frame, 1, &kCFTypeArrayCallBacks);
CFRelease(frame);
if (!frames)
{
WARN("failed to create frames array\n");
return NULL;
}
return frames;
}
/***********************************************************************
* create_cgimage_from_icon
*/
static CGImageRef create_cgimage_from_icon(HDC hdc, HANDLE icon, HBITMAP hbmColor,
unsigned char *color_bits, int color_size, HBITMAP hbmMask,
unsigned char *mask_bits, int mask_size, int width,
int height, int istep)
{
int i, has_alpha = FALSE;
DWORD *ptr;
CGBitmapInfo alpha_format;
CGColorSpaceRef colorspace;
CFDataRef data;
CGDataProviderRef provider;
CGImageRef cgimage;
/* draw the cursor frame to a temporary buffer then create a CGImage from that */
memset(color_bits, 0x00, color_size);
SelectObject(hdc, hbmColor);
if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_NORMAL))
{
WARN("Could not draw frame %d (walk past end of frames).\n", istep);
return NULL;
}
/* check if the cursor frame was drawn with an alpha channel */
for (i = 0, ptr = (DWORD*)color_bits; i < width * height; i++, ptr++)
if ((has_alpha = (*ptr & 0xff000000) != 0)) break;
if (has_alpha)
alpha_format = kCGImageAlphaFirst;
else
alpha_format = kCGImageAlphaNoneSkipFirst;
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
if (!colorspace)
{
WARN("failed to create colorspace\n");
return NULL;
}
data = CFDataCreate(NULL, (UInt8*)color_bits, color_size);
if (!data)
{
WARN("failed to create data\n");
CGColorSpaceRelease(colorspace);
return NULL;
}
provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
if (!provider)
{
WARN("failed to create data provider\n");
CGColorSpaceRelease(colorspace);
return NULL;
}
cgimage = CGImageCreate(width, height, 8, 32, width * 4, colorspace,
alpha_format | kCGBitmapByteOrder32Little,
provider, NULL, FALSE, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorspace);
if (!cgimage)
{
WARN("failed to create image\n");
return NULL;
}
/* if no alpha channel was drawn then generate it from the mask */
if (!has_alpha)
{
unsigned int width_bytes = (width + 31) / 32 * 4;
CGImageRef cgmask, temp;
/* draw the cursor mask to a temporary buffer */
memset(mask_bits, 0xFF, mask_size);
SelectObject(hdc, hbmMask);
if (!DrawIconEx(hdc, 0, 0, icon, width, height, istep, NULL, DI_MASK))
{
WARN("Failed to draw frame mask %d.\n", istep);
CGImageRelease(cgimage);
return NULL;
}
data = CFDataCreate(NULL, (UInt8*)mask_bits, mask_size);
if (!data)
{
WARN("failed to create data\n");
CGImageRelease(cgimage);
return NULL;
}
provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
if (!provider)
{
WARN("failed to create data provider\n");
CGImageRelease(cgimage);
return NULL;
}
cgmask = CGImageMaskCreate(width, height, 1, 1, width_bytes, provider, NULL, FALSE);
CGDataProviderRelease(provider);
if (!cgmask)
{
WARN("failed to create mask\n");
CGImageRelease(cgimage);
return NULL;
}
temp = CGImageCreateWithMask(cgimage, cgmask);
CGImageRelease(cgmask);
CGImageRelease(cgimage);
if (!temp)
{
WARN("failed to create masked image\n");
return NULL;
}
cgimage = temp;
}
return cgimage;
}
/***********************************************************************
* create_cursor_frame
*
* Create a frame dictionary for a cursor from a Windows icon.
* Keys:
* "image" a CGImage for the frame
* "duration" a CFNumber for the frame duration in seconds
* "hotSpot" a CFDictionary encoding a CGPoint for the hot spot
*/
static CFDictionaryRef create_cursor_frame(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon,
HBITMAP hbmColor, unsigned char *color_bits, int color_size,
HBITMAP hbmMask, unsigned char *mask_bits, int mask_size,
int width, int height, int istep)
{
DWORD delay_jiffies, num_steps;
CFMutableDictionaryRef frame;
CGPoint hot_spot;
CFDictionaryRef hot_spot_dict;
double duration;
CFNumberRef duration_number;
CGImageRef cgimage;
TRACE("hdc %p iinfo->xHotspot %d iinfo->yHotspot %d icon %p hbmColor %p color_bits %p color_size %d"
" hbmMask %p mask_bits %p mask_size %d width %d height %d istep %d\n",
hdc, iinfo->xHotspot, iinfo->yHotspot, icon, hbmColor, color_bits, color_size,
hbmMask, mask_bits, mask_size, width, height, istep);
frame = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!frame)
{
WARN("failed to allocate dictionary for frame\n");
return NULL;
}
hot_spot = CGPointMake(iinfo->xHotspot, iinfo->yHotspot);
hot_spot_dict = CGPointCreateDictionaryRepresentation(hot_spot);
if (!hot_spot_dict)
{
WARN("failed to create hot spot dictionary\n");
CFRelease(frame);
return NULL;
}
CFDictionarySetValue(frame, CFSTR("hotSpot"), hot_spot_dict);
CFRelease(hot_spot_dict);
if (GetCursorFrameInfo(icon, 0x0 /* unknown parameter */, istep, &delay_jiffies, &num_steps) != 0)
duration = delay_jiffies / 60.0; /* convert jiffies (1/60s) to seconds */
else
{
WARN("Failed to retrieve animated cursor frame-rate for frame %d.\n", istep);
duration = 0.1; /* fallback delay, 100 ms */
}
duration_number = CFNumberCreate(NULL, kCFNumberDoubleType, &duration);
if (!duration_number)
{
WARN("failed to create duration number\n");
CFRelease(frame);
return NULL;
}
CFDictionarySetValue(frame, CFSTR("duration"), duration_number);
CFRelease(duration_number);
cgimage = create_cgimage_from_icon(hdc, icon, hbmColor, color_bits, color_size,
hbmMask, mask_bits, mask_size, width, height, istep);
if (!cgimage)
{
CFRelease(frame);
return NULL;
}
CFDictionarySetValue(frame, CFSTR("image"), cgimage);
CGImageRelease(cgimage);
return frame;
}
/***********************************************************************
* create_color_cursor
*
* Create an array of color cursor frames from a Windows cursor. Each
* frame is represented in the array by a dictionary.
* Frame dictionary keys:
* "image" a CGImage for the frame
* "duration" a CFNumber for the frame duration in seconds
* "hotSpot" a CFDictionary encoding a CGPoint for the hot spot
*/
static CFArrayRef create_color_cursor(HDC hdc, const ICONINFOEXW *iinfo, HANDLE icon, int width, int height)
{
unsigned char *color_bits, *mask_bits;
HBITMAP hbmColor = 0, hbmMask = 0;
DWORD nFrames, delay_jiffies, i;
int color_size, mask_size;
BITMAPINFO *info = NULL;
CFMutableArrayRef frames;
TRACE("hdc %p iinfo %p icon %p width %d height %d\n", hdc, iinfo, icon, width, height);
/* Retrieve the number of frames to render */
if (!GetCursorFrameInfo(icon, 0x0 /* unknown parameter */, 0, &delay_jiffies, &nFrames))
{
WARN("GetCursorFrameInfo failed\n");
return NULL;
}
if (!(frames = CFArrayCreateMutable(NULL, nFrames, &kCFTypeArrayCallBacks)))
{
WARN("failed to allocate frames array\n");
return NULL;
}
/* Allocate all of the resources necessary to obtain a cursor frame */
if (!(info = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(BITMAPINFO, bmiColors[256])))) goto cleanup;
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biWidth = width;
info->bmiHeader.biHeight = -height;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biCompression = BI_RGB;
info->bmiHeader.biXPelsPerMeter = 0;
info->bmiHeader.biYPelsPerMeter = 0;
info->bmiHeader.biClrUsed = 0;
info->bmiHeader.biClrImportant = 0;
info->bmiHeader.biBitCount = 32;
color_size = width * height * 4;
info->bmiHeader.biSizeImage = color_size;
hbmColor = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (VOID **) &color_bits, NULL, 0);
if (!hbmColor)
{
WARN("failed to create DIB section for cursor color data\n");
goto cleanup;
}
info->bmiHeader.biBitCount = 1;
info->bmiColors[0].rgbRed = 0;
info->bmiColors[0].rgbGreen = 0;
info->bmiColors[0].rgbBlue = 0;
info->bmiColors[0].rgbReserved = 0;
info->bmiColors[1].rgbRed = 0xff;
info->bmiColors[1].rgbGreen = 0xff;
info->bmiColors[1].rgbBlue = 0xff;
info->bmiColors[1].rgbReserved = 0;
mask_size = ((width + 31) / 32 * 4) * height; /* width_bytes * height */
info->bmiHeader.biSizeImage = mask_size;
hbmMask = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (VOID **) &mask_bits, NULL, 0);
if (!hbmMask)
{
WARN("failed to create DIB section for cursor mask data\n");
goto cleanup;
}
/* Create a CFDictionary for each frame of the cursor */
for (i = 0; i < nFrames; i++)
{
CFDictionaryRef frame = create_cursor_frame(hdc, iinfo, icon,
hbmColor, color_bits, color_size,
hbmMask, mask_bits, mask_size,
width, height, i);
if (!frame) goto cleanup;
CFArrayAppendValue(frames, frame);
CFRelease(frame);
}
cleanup:
if (CFArrayGetCount(frames) < nFrames)
{
CFRelease(frames);
frames = NULL;
}
else
TRACE("returning cursor with %d frames\n", nFrames);
/* Cleanup all of the resources used to obtain the frame data */
if (hbmColor) DeleteObject(hbmColor);
if (hbmMask) DeleteObject(hbmMask);
HeapFree(GetProcessHeap(), 0, info);
return frames;
}
/***********************************************************************
* DestroyCursorIcon (MACDRV.@)
*/
void CDECL macdrv_DestroyCursorIcon(HCURSOR cursor)
{
TRACE("cursor %p\n", cursor);
EnterCriticalSection(&cursor_cache_section);
if (cursor_cache)
CFDictionaryRemoveValue(cursor_cache, cursor);
LeaveCriticalSection(&cursor_cache_section);
}
/***********************************************************************
* SetCursor (MACDRV.@)
*/
void CDECL macdrv_SetCursor(HCURSOR cursor)
{
CFStringRef cursor_name = NULL;
CFArrayRef cursor_frames = NULL;
TRACE("%p\n", cursor);
if (cursor)
{
ICONINFOEXW info;
BITMAP bm;
HDC hdc;
EnterCriticalSection(&cursor_cache_section);
if (cursor_cache)
{
CFTypeRef cached_cursor = CFDictionaryGetValue(cursor_cache, cursor);
if (cached_cursor)
{
if (CFGetTypeID(cached_cursor) == CFStringGetTypeID())
cursor_name = CFRetain(cached_cursor);
else
cursor_frames = CFRetain(cached_cursor);
}
}
LeaveCriticalSection(&cursor_cache_section);
if (cursor_name || cursor_frames)
goto done;
info.cbSize = sizeof(info);
if (!GetIconInfoExW(cursor, &info))
{
WARN("GetIconInfoExW failed\n");
return;
}
GetObjectW(info.hbmMask, sizeof(bm), &bm);
if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
/* make sure hotspot is valid */
if (info.xHotspot >= bm.bmWidth || info.yHotspot >= bm.bmHeight)
{
info.xHotspot = bm.bmWidth / 2;
info.yHotspot = bm.bmHeight / 2;
}
hdc = CreateCompatibleDC(0);
if (info.hbmColor)
{
cursor_frames = create_color_cursor(hdc, &info, cursor, bm.bmWidth, bm.bmHeight);
DeleteObject(info.hbmColor);
}
else
cursor_frames = create_monochrome_cursor(hdc, &info, bm.bmWidth, bm.bmHeight);
DeleteObject(info.hbmMask);
DeleteDC(hdc);
if (cursor_name || cursor_frames)
{
EnterCriticalSection(&cursor_cache_section);
if (!cursor_cache)
cursor_cache = CFDictionaryCreateMutable(NULL, 0, NULL,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(cursor_cache, cursor,
cursor_name ? (CFTypeRef)cursor_name : (CFTypeRef)cursor_frames);
LeaveCriticalSection(&cursor_cache_section);
}
else
cursor_name = CFRetain(CFSTR("arrowCursor"));
}
done:
TRACE("setting cursor with cursor_name %s cursor_frames %p\n", debugstr_cf(cursor_name), cursor_frames);
macdrv_set_cursor(cursor_name, cursor_frames);
if (cursor_name) CFRelease(cursor_name);
if (cursor_frames) CFRelease(cursor_frames);
}
/***********************************************************************
* macdrv_mouse_button * macdrv_mouse_button
* *
* Handler for MOUSE_BUTTON events. * Handler for MOUSE_BUTTON events.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
@ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) macdrv_ChangeDisplaySettingsEx @ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) macdrv_ChangeDisplaySettingsEx
@ cdecl CreateDesktopWindow(long) macdrv_CreateDesktopWindow @ cdecl CreateDesktopWindow(long) macdrv_CreateDesktopWindow
@ cdecl CreateWindow(long) macdrv_CreateWindow @ cdecl CreateWindow(long) macdrv_CreateWindow
@ cdecl DestroyCursorIcon(long) macdrv_DestroyCursorIcon
@ cdecl DestroyWindow(long) macdrv_DestroyWindow @ cdecl DestroyWindow(long) macdrv_DestroyWindow
@ cdecl EnumDisplayMonitors(long ptr ptr long) macdrv_EnumDisplayMonitors @ cdecl EnumDisplayMonitors(long ptr ptr long) macdrv_EnumDisplayMonitors
@ cdecl EnumDisplaySettingsEx(ptr long ptr long) macdrv_EnumDisplaySettingsEx @ cdecl EnumDisplaySettingsEx(ptr long ptr long) macdrv_EnumDisplaySettingsEx
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
@ cdecl MapVirtualKeyEx(long long long) macdrv_MapVirtualKeyEx @ cdecl MapVirtualKeyEx(long long long) macdrv_MapVirtualKeyEx
@ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) macdrv_MsgWaitForMultipleObjectsEx @ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) macdrv_MsgWaitForMultipleObjectsEx
@ cdecl ScrollDC(long long long ptr ptr long ptr) macdrv_ScrollDC @ cdecl ScrollDC(long long long ptr ptr long ptr) macdrv_ScrollDC
@ cdecl SetCursor(long) macdrv_SetCursor
@ cdecl SetFocus(long) macdrv_SetFocus @ cdecl SetFocus(long) macdrv_SetFocus
@ cdecl SetLayeredWindowAttributes(long long long long) macdrv_SetLayeredWindowAttributes @ cdecl SetLayeredWindowAttributes(long long long long) macdrv_SetLayeredWindowAttributes
@ cdecl SetParent(long long long) macdrv_SetParent @ cdecl SetParent(long long long) macdrv_SetParent
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment