winemac: Set application Dock icon from first icon resource in process's .exe file.

This commit is contained in:
Ken Thomases 2013-03-17 22:40:49 -05:00 committed by Alexandre Julliard
parent 462721a115
commit b1de532393
6 changed files with 352 additions and 1 deletions

View File

@ -75,6 +75,8 @@ @interface WineApplication : NSApplication <NSApplicationDelegate>
CGPoint synthesizedLocation;
NSTimeInterval lastSetCursorPositionTime;
NSTimeInterval lastEventTapEventTime;
NSImage* applicationIcon;
}
@property (nonatomic) CGEventSourceKeyboardType keyboardType;

View File

@ -58,6 +58,7 @@ @interface WineApplication ()
@property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
@property (copy, nonatomic) NSArray* cursorFrames;
@property (retain, nonatomic) NSTimer* cursorTimer;
@property (retain, nonatomic) NSImage* applicationIcon;
static void PerformRequest(void *info);
@ -67,7 +68,7 @@ @interface WineApplication ()
@implementation WineApplication
@synthesize keyboardType, lastFlagsChanged;
@synthesize orderedWineWindows;
@synthesize orderedWineWindows, applicationIcon;
@synthesize cursorFrames, cursorTimer;
- (id) init
@ -111,6 +112,7 @@ - (id) init
- (void) dealloc
{
[applicationIcon release];
[warpRecords release];
[cursorTimer release];
[cursorFrames release];
@ -169,6 +171,8 @@ - (void) transformProcessToForeground
[self setMainMenu:mainMenu];
[self setWindowsMenu:submenu];
[self setApplicationIconImage:self.applicationIcon];
}
}
@ -631,6 +635,43 @@ - (void) setCursorWithFrames:(NSArray*)frames
}
}
- (void) setApplicationIconFromCGImageArray:(NSArray*)images
{
NSImage* nsimage = nil;
if ([images count])
{
NSSize bestSize = NSZeroSize;
id image;
nsimage = [[[NSImage alloc] initWithSize:NSZeroSize] autorelease];
for (image in images)
{
CGImageRef cgimage = (CGImageRef)image;
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgimage];
if (imageRep)
{
NSSize size = [imageRep size];
[nsimage addRepresentation:imageRep];
[imageRep release];
if (MIN(size.width, size.height) > MIN(bestSize.width, bestSize.height))
bestSize = size;
}
}
if ([[nsimage representations] count] && bestSize.width && bestSize.height)
[nsimage setSize:bestSize];
else
nsimage = nil;
}
self.applicationIcon = nsimage;
[self setApplicationIconImage:nsimage];
}
/*
* ---------- Cursor clipping methods ----------
*
@ -1422,3 +1463,20 @@ int macdrv_clip_cursor(CGRect rect)
return ret;
}
/***********************************************************************
* macdrv_set_application_icon
*
* Set the application icon. The images array contains CGImages. If
* there are more than one, then they represent different sizes or
* color depths from the icon resource. If images is NULL or empty,
* restores the default application image.
*/
void macdrv_set_application_icon(CFArrayRef images)
{
NSArray* imageArray = (NSArray*)images;
OnMainThreadAsync(^{
[NSApp setApplicationIconFromCGImageArray:imageArray];
});
}

View File

@ -25,6 +25,30 @@
WINE_DEFAULT_DEBUG_CHANNEL(image);
#include "pshpack1.h"
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
WORD nID;
} GRPICONDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
GRPICONDIRENTRY idEntries[1];
} GRPICONDIR;
#include "poppack.h"
/***********************************************************************
* create_cgimage_from_icon_bitmaps
@ -150,3 +174,253 @@ CGImageRef create_cgimage_from_icon_bitmaps(HDC hdc, HANDLE icon, HBITMAP hbmCol
return cgimage;
}
/***********************************************************************
* create_cgimage_from_icon
*
* Create a CGImage from a Windows icon.
*/
static CGImageRef create_cgimage_from_icon(HANDLE icon, int width, int height)
{
CGImageRef ret = NULL;
HDC hdc;
char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
BITMAPINFO *bitmapinfo = (BITMAPINFO*)buffer;
unsigned char *color_bits, *mask_bits;
HBITMAP hbmColor = 0, hbmMask = 0;
int color_size, mask_size;
TRACE("icon %p width %d height %d\n", icon, width, height);
if (!width && !height)
{
ICONINFO info;
BITMAP bm;
if (!GetIconInfo(icon, &info))
return NULL;
GetObjectW(info.hbmMask, sizeof(bm), &bm);
if (!info.hbmColor) bm.bmHeight = max(1, bm.bmHeight / 2);
width = bm.bmWidth;
height = bm.bmHeight;
DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);
}
hdc = CreateCompatibleDC(0);
bitmapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfo->bmiHeader.biWidth = width;
bitmapinfo->bmiHeader.biHeight = -height;
bitmapinfo->bmiHeader.biPlanes = 1;
bitmapinfo->bmiHeader.biCompression = BI_RGB;
bitmapinfo->bmiHeader.biXPelsPerMeter = 0;
bitmapinfo->bmiHeader.biYPelsPerMeter = 0;
bitmapinfo->bmiHeader.biClrUsed = 0;
bitmapinfo->bmiHeader.biClrImportant = 0;
bitmapinfo->bmiHeader.biBitCount = 32;
color_size = width * height * 4;
bitmapinfo->bmiHeader.biSizeImage = color_size;
hbmColor = CreateDIBSection(hdc, bitmapinfo, DIB_RGB_COLORS, (VOID **) &color_bits, NULL, 0);
if (!hbmColor)
{
WARN("failed to create DIB section for cursor color data\n");
goto cleanup;
}
bitmapinfo->bmiHeader.biBitCount = 1;
bitmapinfo->bmiColors[0].rgbRed = 0;
bitmapinfo->bmiColors[0].rgbGreen = 0;
bitmapinfo->bmiColors[0].rgbBlue = 0;
bitmapinfo->bmiColors[0].rgbReserved = 0;
bitmapinfo->bmiColors[1].rgbRed = 0xff;
bitmapinfo->bmiColors[1].rgbGreen = 0xff;
bitmapinfo->bmiColors[1].rgbBlue = 0xff;
bitmapinfo->bmiColors[1].rgbReserved = 0;
mask_size = ((width + 31) / 32 * 4) * height;
bitmapinfo->bmiHeader.biSizeImage = mask_size;
hbmMask = CreateDIBSection(hdc, bitmapinfo, DIB_RGB_COLORS, (VOID **) &mask_bits, NULL, 0);
if (!hbmMask)
{
WARN("failed to create DIB section for cursor mask data\n");
goto cleanup;
}
ret = create_cgimage_from_icon_bitmaps(hdc, icon, hbmColor, color_bits, color_size, hbmMask,
mask_bits, mask_size, width, height, 0);
cleanup:
if (hbmColor) DeleteObject(hbmColor);
if (hbmMask) DeleteObject(hbmMask);
DeleteDC(hdc);
return ret;
}
/***********************************************************************
* get_first_resource
*
* Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
* which just gets the handle for the first resource and stops further
* enumeration.
*/
static BOOL CALLBACK get_first_resource(HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR lparam)
{
HRSRC *res_info = (HRSRC*)lparam;
*res_info = FindResourceW(module, name, (LPCWSTR)RT_GROUP_ICON);
return FALSE;
}
/***********************************************************************
* create_app_icon_images
*/
CFArrayRef create_app_icon_images(void)
{
HRSRC res_info;
HGLOBAL res_data;
GRPICONDIR *icon_dir;
CFMutableArrayRef images = NULL;
int i;
TRACE("()\n");
res_info = NULL;
EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
if (!res_info)
{
WARN("found no RT_GROUP_ICON resource\n");
return NULL;
}
if (!(res_data = LoadResource(NULL, res_info)))
{
WARN("failed to load RT_GROUP_ICON resource\n");
return NULL;
}
if (!(icon_dir = LockResource(res_data)))
{
WARN("failed to lock RT_GROUP_ICON resource\n");
goto cleanup;
}
images = CFArrayCreateMutable(NULL, icon_dir->idCount, &kCFTypeArrayCallBacks);
if (!images)
{
WARN("failed to create images array\n");
goto cleanup;
}
for (i = 0; i < icon_dir->idCount; i++)
{
int width = icon_dir->idEntries[i].bWidth;
int height = icon_dir->idEntries[i].bHeight;
BOOL found_better_bpp = FALSE;
int j;
LPCWSTR name;
HGLOBAL icon_res_data;
BYTE *icon_bits;
if (!width) width = 256;
if (!height) height = 256;
/* If there's another icon at the same size but with better
color depth, skip this one. We end up making CGImages that
are all 32 bits per pixel, so Cocoa doesn't get the original
color depth info to pick the best representation itself. */
for (j = 0; j < icon_dir->idCount; j++)
{
int jwidth = icon_dir->idEntries[j].bWidth;
int jheight = icon_dir->idEntries[j].bHeight;
if (!jwidth) jwidth = 256;
if (!jheight) jheight = 256;
if (j != i && jwidth == width && jheight == height &&
icon_dir->idEntries[j].wBitCount > icon_dir->idEntries[i].wBitCount)
{
found_better_bpp = TRUE;
break;
}
}
if (found_better_bpp) continue;
name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
if (!res_info)
{
WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
continue;
}
icon_res_data = LoadResource(NULL, res_info);
if (!icon_res_data)
{
WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
continue;
}
icon_bits = LockResource(icon_res_data);
if (icon_bits)
{
static const BYTE png_magic[] = { 0x89, 0x50, 0x4e, 0x47 };
CGImageRef cgimage = NULL;
if (!memcmp(icon_bits, png_magic, sizeof(png_magic)))
{
CFDataRef data = CFDataCreate(NULL, (UInt8*)icon_bits, icon_dir->idEntries[i].dwBytesInRes);
if (data)
{
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
if (provider)
{
cgimage = CGImageCreateWithPNGDataProvider(provider, NULL, FALSE,
kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
}
}
}
if (!cgimage)
{
HICON icon;
icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
TRUE, 0x00030000, width, height, 0);
if (icon)
{
cgimage = create_cgimage_from_icon(icon, width, height);
DestroyIcon(icon);
}
else
WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
}
if (cgimage)
{
CFArrayAppendValue(images, cgimage);
CGImageRelease(cgimage);
}
}
else
WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
FreeResource(icon_res_data);
}
cleanup:
if (images && !CFArrayGetCount(images))
{
CFRelease(images);
images = NULL;
}
FreeResource(res_data);
return images;
}

View File

@ -177,5 +177,6 @@ extern CGImageRef create_cgimage_from_icon_bitmaps(HDC hdc, HANDLE icon, HBITMAP
unsigned char *color_bits, int color_size, HBITMAP hbmMask,
unsigned char *mask_bits, int mask_size, int width,
int height, int istep) DECLSPEC_HIDDEN;
extern CFArrayRef create_app_icon_images(void) DECLSPEC_HIDDEN;
#endif /* __WINE_MACDRV_H */

View File

@ -132,6 +132,7 @@
extern int macdrv_start_cocoa_app(unsigned long long tickcount) DECLSPEC_HIDDEN;
extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_beep(void) DECLSPEC_HIDDEN;
extern void macdrv_set_application_icon(CFArrayRef images) DECLSPEC_HIDDEN;
/* cursor */

View File

@ -66,6 +66,20 @@ const char* debugstr_cf(CFTypeRef t)
}
/***********************************************************************
* set_app_icon
*/
static void set_app_icon(void)
{
CFArrayRef images = create_app_icon_images();
if (images)
{
macdrv_set_application_icon(images);
CFRelease(images);
}
}
/***********************************************************************
* process_attach
*/
@ -89,6 +103,7 @@ static BOOL process_attach(void)
return FALSE;
}
set_app_icon();
macdrv_clipboard_process_attach();
return TRUE;