/* * 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 #include #include "windows.h" #include "callback.h" #include "cursoricon.h" #include "sysmetrics.h" #include "win.h" #include "bitmap.h" #include "struct32.h" #include "heap.h" #include "task.h" #include "stddebug.h" #include "debug.h" #include "xmalloc.h" /* This dictionary could might eventually become a macro for better reuse */ struct MAP_DWORD_DWORD { DWORD key; HANDLE32 value; }; struct MAP_DWORD_DWORD *CURSORICON_map; int CURSORICON_count; BOOL32 CURSORICON_lookup(DWORD key,HANDLE32 *value) { int i; for(i=0;iidCount < 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 BOOL32 CURSORICON32_LoadDirEntry(HINSTANCE32 hInstance, LPCWSTR name, int width, int height, int colors, BOOL32 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 HGLOBAL32 CURSORICON32_LoadHandler( HANDLE32 handle, HINSTANCE32 hInstance, BOOL32 fCursor ) { HBITMAP32 hAndBits, hXorBits; HGLOBAL32 hRes; HDC32 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 = GetDC32( 0 ))) { free( pInfo ); return 0; } hXorBits = CreateDIBitmap32( 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_GetDIBWidthBytes(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_GetDIBWidthBytes(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 = CreateDIBitmap32( hdc, &pInfo->bmiHeader, CBM_INIT, bits, pInfo, DIB_RGB_COLORS ); ReleaseDC32( 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))) { DeleteObject32( hXorBits ); DeleteObject32( 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 */ GetBitmapBits32( hAndBits, sizeAnd, (char *)(info + 1) ); GetBitmapBits32( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd ); DeleteObject32( hXorBits ); DeleteObject32( hAndBits ); GlobalUnlock16( hRes ); return hRes; } /********************************************************************** * CURSORICON32_Load * * Load a cursor or icon. */ static HGLOBAL32 CURSORICON32_Load( HINSTANCE32 hInstance, LPCWSTR name, int width, int height, int colors, BOOL32 fCursor ) { HANDLE32 handle; HANDLE32 hRet; HANDLE32 hRsrc; CURSORICONDIRENTRY32 dirEntry; if(!hInstance) /* OEM cursor/icon */ { WORD resid; if(HIWORD(name)) { LPSTR ansi = HEAP_strdupWtoA(GetProcessHeap(),0,name); if(ansi[0]=='#') /*Check for '#xxx' name */ { resid = atoi(ansi+1); HeapFree( GetProcessHeap(), 0, ansi ); } else { HeapFree( GetProcessHeap(), 0, ansi ); return 0; } } else resid = LOWORD(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 WINAPI LoadCursor32W(HINSTANCE32 hInstance,LPCWSTR name) { return CURSORICON32_Load( hInstance, name, SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE); } /*********************************************************************** * LoadCursorA (USER32.358) */ HCURSOR32 WINAPI LoadCursor32A(HINSTANCE32 hInstance,LPCSTR name) { HCURSOR32 res=0; if(!HIWORD(name)) return LoadCursor32W(hInstance,(LPCWSTR)name); else { LPWSTR uni = HEAP_strdupAtoW( GetProcessHeap(), 0, name ); res = LoadCursor32W(hInstance, uni); HeapFree( GetProcessHeap(), 0, uni); } return res; } /*********************************************************************** * LoadIconW (USER32.363) */ HICON32 WINAPI LoadIcon32W(HINSTANCE32 hInstance,LPCWSTR name) { return CURSORICON32_Load( hInstance, name, SYSMETRICS_CXICON, SYSMETRICS_CYICON, MIN( 16, 1 << screenDepth ), FALSE ); } /*********************************************************************** * LoadIconA (USER32.362) */ HICON32 WINAPI LoadIcon32A(HINSTANCE32 hInstance,LPCSTR name) { HICON32 res=0; if(!HIWORD(name)) return LoadIcon32W(hInstance, (LPCWSTR)name); else { LPWSTR uni = HEAP_strdupAtoW( GetProcessHeap(), 0, name ); res = LoadIcon32W( hInstance, uni ); HeapFree( GetProcessHeap(), 0, uni ); } return res; }