| /* |
| * Cursor and icon support |
| * |
| * Copyright 1995 Alexandre Julliard |
| * Copyright 1996 Martin von Loewis |
| */ |
| |
| /* |
| * Theory: |
| * |
| * Cursors and icons are stored in a global heap block, with the |
| * following layout: |
| * |
| * CURSORICONINFO info; |
| * BYTE[] ANDbits; |
| * BYTE[] XORbits; |
| * |
| * The bits structures are in the format of a device-dependent bitmap. |
| * |
| * This layout is very sub-optimal, as the bitmap bits are stored in |
| * the X client instead of in the server like other bitmaps; however, |
| * some programs (notably Paint Brush) expect to be able to manipulate |
| * the bits directly :-( |
| */ |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include "windows.h" |
| #include "bitmap.h" |
| #include "callback.h" |
| #include "cursoricon.h" |
| #include "sysmetrics.h" |
| #include "win.h" |
| #include "struct32.h" |
| #include "string32.h" |
| #include "stddebug.h" |
| #include "debug.h" |
| #include "xmalloc.h" |
| #include "task.h" |
| |
| /* This dictionary could might eventually become a macro for better reuse */ |
| struct MAP_DWORD_DWORD{ |
| DWORD key; |
| DWORD value; |
| }; |
| |
| struct MAP_DWORD_DWORD *CURSORICON_map; |
| int CURSORICON_count; |
| |
| BOOL CURSORICON_lookup(DWORD key,DWORD *value) |
| { |
| int i; |
| for(i=0;i<CURSORICON_count;i++) |
| { |
| if(key==CURSORICON_map[i].key) |
| { |
| *value=CURSORICON_map[i].value; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| void CURSORICON_insert(DWORD key,DWORD value) |
| { |
| if(!CURSORICON_count) |
| { |
| CURSORICON_count=1; |
| CURSORICON_map=malloc(sizeof(struct MAP_DWORD_DWORD)); |
| }else{ |
| CURSORICON_count++; |
| CURSORICON_map=realloc(CURSORICON_map, |
| sizeof(struct MAP_DWORD_DWORD)*CURSORICON_count); |
| } |
| CURSORICON_map[CURSORICON_count-1].key=key; |
| CURSORICON_map[CURSORICON_count-1].value=value; |
| } |
| |
| /********************************************************************** |
| * CURSORICON32_FindBestIcon |
| * |
| * Find the icon closest to the requested size and number of colors. |
| */ |
| static ICONDIRENTRY32 *CURSORICON32_FindBestIcon( CURSORICONDIR32 *dir, |
| int width, int height, int colors ) |
| { |
| int i, maxcolors, maxwidth, maxheight; |
| ICONDIRENTRY32 *entry, *bestEntry = NULL; |
| |
| if (dir->idCount < 1) |
| { |
| fprintf( stderr, "Icon: empty directory!\n" ); |
| return NULL; |
| } |
| if (dir->idCount == 1) return &dir->idEntries[0].icon; /* No choice... */ |
| |
| /* First find the exact size with less colors */ |
| |
| maxcolors = 0; |
| for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++) |
| if ((entry->bWidth == width) && (entry->bHeight == height) && |
| (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors)) |
| { |
| bestEntry = entry; |
| maxcolors = entry->bColorCount; |
| } |
| if (bestEntry) return bestEntry; |
| |
| /* First find the exact size with more colors */ |
| |
| maxcolors = 255; |
| for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++) |
| if ((entry->bWidth == width) && (entry->bHeight == height) && |
| (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors)) |
| { |
| bestEntry = entry; |
| maxcolors = entry->bColorCount; |
| } |
| if (bestEntry) return bestEntry; |
| |
| /* Now find a smaller one with less colors */ |
| |
| maxcolors = maxwidth = maxheight = 0; |
| for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++) |
| if ((entry->bWidth <= width) && (entry->bHeight <= height) && |
| (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) && |
| (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors)) |
| { |
| bestEntry = entry; |
| maxwidth = entry->bWidth; |
| maxheight = entry->bHeight; |
| maxcolors = entry->bColorCount; |
| } |
| if (bestEntry) return bestEntry; |
| |
| /* Now find a smaller one with more colors */ |
| |
| maxcolors = 255; |
| maxwidth = maxheight = 0; |
| for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++) |
| if ((entry->bWidth <= width) && (entry->bHeight <= height) && |
| (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) && |
| (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors)) |
| { |
| bestEntry = entry; |
| maxwidth = entry->bWidth; |
| maxheight = entry->bHeight; |
| maxcolors = entry->bColorCount; |
| } |
| if (bestEntry) return bestEntry; |
| |
| /* Now find a larger one with less colors */ |
| |
| maxcolors = 0; |
| maxwidth = maxheight = 255; |
| for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++) |
| if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) && |
| (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors)) |
| { |
| bestEntry = entry; |
| maxwidth = entry->bWidth; |
| maxheight = entry->bHeight; |
| maxcolors = entry->bColorCount; |
| } |
| if (bestEntry) return bestEntry; |
| |
| /* Now find a larger one with more colors */ |
| |
| maxcolors = maxwidth = maxheight = 255; |
| for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++) |
| if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) && |
| (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors)) |
| { |
| bestEntry = entry; |
| maxwidth = entry->bWidth; |
| maxheight = entry->bHeight; |
| maxcolors = entry->bColorCount; |
| } |
| |
| return bestEntry; |
| } |
| |
| |
| /********************************************************************** |
| * CURSORICON32_FindBestCursor |
| * |
| * Find the cursor closest to the requested size. |
| */ |
| static CURSORDIRENTRY32 *CURSORICON32_FindBestCursor( CURSORICONDIR32 *dir, |
| int width, int height ) |
| { |
| int i, maxwidth, maxheight; |
| CURSORDIRENTRY32 *entry, *bestEntry = NULL; |
| |
| if (dir->idCount < 1) |
| { |
| fprintf( stderr, "Cursor: empty directory!\n" ); |
| return NULL; |
| } |
| if (dir->idCount == 1) return &dir->idEntries[0].cursor; /* No choice... */ |
| |
| /* First find the largest one smaller than or equal to the requested size*/ |
| |
| maxwidth = maxheight = 0; |
| for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++) |
| if ((entry->wWidth <= width) && (entry->wHeight <= height) && |
| (entry->wWidth > maxwidth) && (entry->wHeight > maxheight)) |
| { |
| bestEntry = entry; |
| maxwidth = entry->wWidth; |
| maxheight = entry->wHeight; |
| } |
| if (bestEntry) return bestEntry; |
| |
| /* Now find the smallest one larger than the requested size */ |
| |
| maxwidth = maxheight = 255; |
| for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++) |
| if ((entry->wWidth < maxwidth) && (entry->wHeight < maxheight)) |
| { |
| bestEntry = entry; |
| maxwidth = entry->wWidth; |
| maxheight = entry->wHeight; |
| } |
| |
| return bestEntry; |
| } |
| |
| |
| /********************************************************************** |
| * CURSORICON32_LoadDirEntry |
| * |
| * Load the icon/cursor directory for a given resource name and find the |
| * best matching entry. |
| */ |
| static BOOL CURSORICON32_LoadDirEntry(HANDLE hInstance, LPCWSTR name, |
| int width, int height, int colors, |
| BOOL fCursor, CURSORICONDIRENTRY32 *dirEntry) |
| { |
| HANDLE32 hRsrc; |
| HANDLE32 hMem; |
| CURSORICONDIR32 *dir; |
| CURSORICONDIRENTRY32 *entry = NULL; |
| |
| if (!(hRsrc = FindResource32W( hInstance, name, |
| (LPCWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) ))) |
| return FALSE; |
| if (!(hMem = LoadResource32( hInstance, hRsrc ))) return FALSE; |
| if ((dir = (CURSORICONDIR32 *)LockResource32( hMem ))) |
| { |
| if (fCursor) |
| entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestCursor( dir, |
| width, height ); |
| else |
| entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestIcon( dir, |
| width, height, colors ); |
| if (entry) *dirEntry = *entry; |
| } |
| FreeResource32( hMem ); |
| return (entry != NULL); |
| } |
| |
| |
| /********************************************************************** |
| * CURSORICON32_LoadHandler |
| * |
| * Create a cursor or icon from a resource. |
| */ |
| static HANDLE CURSORICON32_LoadHandler( HANDLE32 handle, HINSTANCE hInstance, |
| BOOL fCursor ) |
| { |
| HANDLE hAndBits, hXorBits, hRes; |
| HDC hdc; |
| int size, sizeAnd, sizeXor; |
| POINT16 hotspot = { 0 ,0 }; |
| BITMAPOBJ *bmpXor, *bmpAnd; |
| BITMAPINFO *bmi, *pInfo; |
| CURSORICONINFO *info; |
| char *bits; |
| |
| hRes=0; |
| if (fCursor) /* If cursor, get the hotspot */ |
| { |
| POINT16 *pt = (POINT16 *)LockResource32( handle ); |
| hotspot = *pt; |
| bmi = (BITMAPINFO *)(pt + 1); |
| } |
| else bmi = (BITMAPINFO *)LockResource32( handle ); |
| |
| /* Create a copy of the bitmap header */ |
| |
| size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS ); |
| /* Make sure we have room for the monochrome bitmap later on */ |
| size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) ); |
| pInfo = (BITMAPINFO *)xmalloc( size ); |
| memcpy( pInfo, bmi, size ); |
| |
| if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER)) |
| { |
| if (pInfo->bmiHeader.biCompression != BI_RGB) |
| { |
| fprintf(stderr,"Unknown size for compressed icon bitmap.\n"); |
| free( pInfo ); |
| return 0; |
| } |
| pInfo->bmiHeader.biHeight /= 2; |
| } |
| else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER)) |
| { |
| BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo; |
| core->bcHeight /= 2; |
| } |
| else |
| { |
| fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n", |
| pInfo->bmiHeader.biSize ); |
| free( pInfo ); |
| return 0; |
| } |
| |
| /* Create the XOR bitmap */ |
| |
| if (!(hdc = GetDC( 0 ))) |
| { |
| free( pInfo ); |
| return 0; |
| } |
| |
| hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT, |
| (char*)bmi + size, pInfo, DIB_RGB_COLORS ); |
| |
| /* Fix the bitmap header to load the monochrome mask */ |
| |
| if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER)) |
| { |
| BITMAPINFOHEADER *bih = &pInfo->bmiHeader; |
| RGBQUAD *rgb = pInfo->bmiColors; |
| bits = (char *)bmi + size + |
| DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight; |
| bih->biBitCount = 1; |
| bih->biClrUsed = bih->biClrImportant = 2; |
| rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00; |
| rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff; |
| rgb[0].rgbReserved = rgb[1].rgbReserved = 0; |
| } |
| else |
| { |
| BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo; |
| RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1); |
| bits = (char *)bmi + size + |
| DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight; |
| bch->bcBitCount = 1; |
| rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00; |
| rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff; |
| } |
| |
| /* Create the AND bitmap */ |
| |
| hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT, |
| bits, pInfo, DIB_RGB_COLORS ); |
| ReleaseDC( 0, hdc ); |
| |
| /* Now create the CURSORICONINFO structure */ |
| |
| bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC ); |
| bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC ); |
| sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes; |
| sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes; |
| |
| if (!(hRes = GlobalAlloc16( GMEM_MOVEABLE, |
| sizeof(CURSORICONINFO) + sizeXor + sizeAnd))) |
| { |
| DeleteObject( hXorBits ); |
| DeleteObject( hAndBits ); |
| return 0; |
| } |
| |
| /* Make it owned by the module */ |
| if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) ); |
| |
| info = (CURSORICONINFO *)GlobalLock16( hRes ); |
| info->ptHotSpot.x = hotspot.x; |
| info->ptHotSpot.y = hotspot.y; |
| info->nWidth = bmpXor->bitmap.bmWidth; |
| info->nHeight = bmpXor->bitmap.bmHeight; |
| info->nWidthBytes = bmpXor->bitmap.bmWidthBytes; |
| info->bPlanes = bmpXor->bitmap.bmPlanes; |
| info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel; |
| |
| /* Transfer the bitmap bits to the CURSORICONINFO structure */ |
| |
| GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) ); |
| GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd ); |
| DeleteObject( hXorBits ); |
| DeleteObject( hAndBits ); |
| GlobalUnlock16( hRes ); |
| return hRes; |
| } |
| |
| /********************************************************************** |
| * CURSORICON32_Load |
| * |
| * Load a cursor or icon. |
| */ |
| static HANDLE CURSORICON32_Load( HANDLE hInstance, LPCWSTR name, int width, |
| int height, int colors, BOOL fCursor ) |
| { |
| HANDLE32 handle; |
| HANDLE hRet; |
| HANDLE32 hRsrc; |
| CURSORICONDIRENTRY32 dirEntry; |
| |
| if(!hInstance) /* OEM cursor/icon */ |
| { |
| WORD resid; |
| if(HIWORD(name)) |
| { |
| LPSTR ansi; |
| ansi=STRING32_DupUniToAnsi(name); |
| if(ansi[0]=='#') /*Check for '#xxx' name */ |
| { |
| resid=atoi(ansi+1); |
| free(ansi); |
| }else{ |
| free(ansi); |
| return 0; |
| } |
| } |
| else |
| resid=(WORD)(int)name; |
| return OBM_LoadCursorIcon(resid, fCursor); |
| } |
| |
| /* Find the best entry in the directory */ |
| |
| if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height, |
| colors, fCursor, &dirEntry )) return 0; |
| |
| /* Load the resource */ |
| |
| if (!(hRsrc = FindResource32W( hInstance, |
| (LPWSTR) (DWORD) dirEntry.icon.wResId, |
| (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0; |
| if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0; |
| |
| /* Use the resource handle as key to detect multiple loading */ |
| if(CURSORICON_lookup(handle,&hRet)) |
| return hRet; |
| |
| hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor ); |
| /* Obsolete - FreeResource32(handle);*/ |
| CURSORICON_insert(handle,hRet); |
| return hRet; |
| } |
| |
| |
| /*********************************************************************** |
| * LoadCursorW (USER32.361) |
| */ |
| HCURSOR32 LoadCursor32W(HINSTANCE32 hInstance,LPCWSTR name) |
| { |
| return CURSORICON32_Load( hInstance, name, |
| SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE); |
| } |
| |
| /*********************************************************************** |
| * LoadCursorA (USER32.358) |
| */ |
| HCURSOR32 LoadCursor32A(HINSTANCE32 hInstance,LPCSTR name) |
| { |
| HCURSOR32 res=0; |
| if(!HIWORD(name)) |
| return LoadCursor32W(hInstance,(LPCWSTR)name); |
| else { |
| LPWSTR uni = STRING32_DupAnsiToUni(name); |
| res = LoadCursor32W(hInstance, uni); |
| free(uni); |
| } |
| return res; |
| } |
| |
| |
| /*********************************************************************** |
| * LoadIconW (USER32.363) |
| */ |
| HICON32 LoadIcon32W(HINSTANCE32 hInstance,LPCWSTR name) |
| { |
| return CURSORICON32_Load( hInstance, name, |
| SYSMETRICS_CXICON, SYSMETRICS_CYICON, |
| MIN( 16, 1 << screenDepth ), FALSE ); |
| } |
| |
| /*********************************************************************** |
| * LoadIconA (USER32.362) |
| */ |
| HICON32 LoadIcon32A(HINSTANCE32 hInstance,LPCSTR name) |
| { |
| HICON32 res=0; |
| if(!HIWORD(name)) |
| return LoadIcon32W(hInstance, (LPCWSTR)name); |
| else { |
| LPWSTR uni = STRING32_DupAnsiToUni(name); |
| res = LoadIcon32W(hInstance, uni); |
| free(uni); |
| } |
| return res; |
| } |
| |