winemac: Implement SetCursor() and DestroyCursorIcon().
This commit is contained in:
parent
1896e5575e
commit
a49df4b317
|
@ -50,6 +50,11 @@ @interface WineApplication : NSApplication <NSApplicationDelegate>
|
|||
NSMutableArray* orderedWineWindows;
|
||||
|
||||
NSMutableDictionary* originalDisplayModes;
|
||||
|
||||
NSArray* cursorFrames;
|
||||
int cursorFrame;
|
||||
NSTimer* cursorTimer;
|
||||
BOOL cursorHidden;
|
||||
}
|
||||
|
||||
@property (nonatomic) CGEventSourceKeyboardType keyboardType;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
@interface WineApplication ()
|
||||
|
||||
@property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
|
||||
@property (copy, nonatomic) NSArray* cursorFrames;
|
||||
@property (retain, nonatomic) NSTimer* cursorTimer;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -39,6 +41,7 @@ @implementation WineApplication
|
|||
|
||||
@synthesize keyboardType, lastFlagsChanged;
|
||||
@synthesize orderedWineWindows;
|
||||
@synthesize cursorFrames, cursorTimer;
|
||||
|
||||
- (id) init
|
||||
{
|
||||
|
@ -65,6 +68,8 @@ - (id) init
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
[cursorTimer release];
|
||||
[cursorFrames release];
|
||||
[originalDisplayModes release];
|
||||
[orderedWineWindows release];
|
||||
[keyWindows release];
|
||||
|
@ -460,6 +465,89 @@ - (BOOL) areDisplaysCaptured
|
|||
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 ----------
|
||||
|
@ -673,3 +761,57 @@ int macdrv_set_display_mode(const struct macdrv_display* display,
|
|||
|
||||
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 @@
|
|||
#include "wine/gdi_driver.h"
|
||||
|
||||
|
||||
extern const char* debugstr_cf(CFTypeRef t) DECLSPEC_HIDDEN;
|
||||
|
||||
static inline CGRect cgrect_from_rect(RECT rect)
|
||||
{
|
||||
if (rect.left >= rect.right || rect.top >= rect.bottom)
|
||||
|
|
|
@ -118,6 +118,10 @@
|
|||
extern void macdrv_beep(void) DECLSPEC_HIDDEN;
|
||||
|
||||
|
||||
/* cursor */
|
||||
extern void macdrv_set_cursor(CFStringRef name, CFArrayRef frames) DECLSPEC_HIDDEN;
|
||||
|
||||
|
||||
/* display */
|
||||
extern int macdrv_get_displays(struct macdrv_display** displays, int* count) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_free_displays(struct macdrv_display* displays) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -30,6 +30,40 @@ WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -23,12 +23,27 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "macdrv.h"
|
||||
#define OEMRESOURCE
|
||||
#include "winuser.h"
|
||||
#include "winreg.h"
|
||||
#include "wine/server.h"
|
||||
#include "wine/unicode.h"
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -74,6 +89,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
|
||||
*
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
@ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) macdrv_ChangeDisplaySettingsEx
|
||||
@ cdecl CreateDesktopWindow(long) macdrv_CreateDesktopWindow
|
||||
@ cdecl CreateWindow(long) macdrv_CreateWindow
|
||||
@ cdecl DestroyCursorIcon(long) macdrv_DestroyCursorIcon
|
||||
@ cdecl DestroyWindow(long) macdrv_DestroyWindow
|
||||
@ cdecl EnumDisplayMonitors(long ptr ptr long) macdrv_EnumDisplayMonitors
|
||||
@ cdecl EnumDisplaySettingsEx(ptr long ptr long) macdrv_EnumDisplaySettingsEx
|
||||
|
@ -19,6 +20,7 @@
|
|||
@ cdecl MapVirtualKeyEx(long long long) macdrv_MapVirtualKeyEx
|
||||
@ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) macdrv_MsgWaitForMultipleObjectsEx
|
||||
@ cdecl ScrollDC(long long long ptr ptr long ptr) macdrv_ScrollDC
|
||||
@ cdecl SetCursor(long) macdrv_SetCursor
|
||||
@ cdecl SetFocus(long) macdrv_SetFocus
|
||||
@ cdecl SetLayeredWindowAttributes(long long long long) macdrv_SetLayeredWindowAttributes
|
||||
@ cdecl SetParent(long long long) macdrv_SetParent
|
||||
|
|
Loading…
Reference in New Issue