2 * Cursor and icon support
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Martin von Loewis
11 * Cursors and icons are stored in a global heap block, with the
14 * CURSORICONINFO info;
18 * The bits structures are in the format of a device-dependent bitmap.
20 * This layout is very sub-optimal, as the bitmap bits are stored in
21 * the X client instead of in the server like other bitmaps; however,
22 * some programs (notably Paint Brush) expect to be able to manipulate
23 * the bits directly :-(
31 #include "cursoricon.h"
32 #include "sysmetrics.h"
41 /* This dictionary could might eventually become a macro for better reuse */
42 struct MAP_DWORD_DWORD
{
47 struct MAP_DWORD_DWORD
*CURSORICON_map
;
50 BOOL
CURSORICON_lookup(DWORD key
,DWORD
*value
)
53 for(i
=0;i
<CURSORICON_count
;i
++)
55 if(key
==CURSORICON_map
[i
].key
)
57 *value
=CURSORICON_map
[i
].value
;
64 void CURSORICON_insert(DWORD key
,DWORD value
)
69 CURSORICON_map
=malloc(sizeof(struct MAP_DWORD_DWORD
));
72 CURSORICON_map
=realloc(CURSORICON_map
,
73 sizeof(struct MAP_DWORD_DWORD
)*CURSORICON_count
);
75 CURSORICON_map
[CURSORICON_count
-1].key
=key
;
76 CURSORICON_map
[CURSORICON_count
-1].value
=value
;
79 /**********************************************************************
80 * CURSORICON32_FindBestIcon
82 * Find the icon closest to the requested size and number of colors.
84 static ICONDIRENTRY32
*CURSORICON32_FindBestIcon( CURSORICONDIR32
*dir
,
85 int width
, int height
, int colors
)
87 int i
, maxcolors
, maxwidth
, maxheight
;
88 ICONDIRENTRY32
*entry
, *bestEntry
= NULL
;
92 fprintf( stderr
, "Icon: empty directory!\n" );
95 if (dir
->idCount
== 1) return &dir
->idEntries
[0].icon
; /* No choice... */
97 /* First find the exact size with less colors */
100 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
101 if ((entry
->bWidth
== width
) && (entry
->bHeight
== height
) &&
102 (entry
->bColorCount
<= colors
) && (entry
->bColorCount
> maxcolors
))
105 maxcolors
= entry
->bColorCount
;
107 if (bestEntry
) return bestEntry
;
109 /* First find the exact size with more colors */
112 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
113 if ((entry
->bWidth
== width
) && (entry
->bHeight
== height
) &&
114 (entry
->bColorCount
> colors
) && (entry
->bColorCount
<= maxcolors
))
117 maxcolors
= entry
->bColorCount
;
119 if (bestEntry
) return bestEntry
;
121 /* Now find a smaller one with less colors */
123 maxcolors
= maxwidth
= maxheight
= 0;
124 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
125 if ((entry
->bWidth
<= width
) && (entry
->bHeight
<= height
) &&
126 (entry
->bWidth
>= maxwidth
) && (entry
->bHeight
>= maxheight
) &&
127 (entry
->bColorCount
<= colors
) && (entry
->bColorCount
> maxcolors
))
130 maxwidth
= entry
->bWidth
;
131 maxheight
= entry
->bHeight
;
132 maxcolors
= entry
->bColorCount
;
134 if (bestEntry
) return bestEntry
;
136 /* Now find a smaller one with more colors */
139 maxwidth
= maxheight
= 0;
140 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
141 if ((entry
->bWidth
<= width
) && (entry
->bHeight
<= height
) &&
142 (entry
->bWidth
>= maxwidth
) && (entry
->bHeight
>= maxheight
) &&
143 (entry
->bColorCount
> colors
) && (entry
->bColorCount
<= maxcolors
))
146 maxwidth
= entry
->bWidth
;
147 maxheight
= entry
->bHeight
;
148 maxcolors
= entry
->bColorCount
;
150 if (bestEntry
) return bestEntry
;
152 /* Now find a larger one with less colors */
155 maxwidth
= maxheight
= 255;
156 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
157 if ((entry
->bWidth
<= maxwidth
) && (entry
->bHeight
<= maxheight
) &&
158 (entry
->bColorCount
<= colors
) && (entry
->bColorCount
> maxcolors
))
161 maxwidth
= entry
->bWidth
;
162 maxheight
= entry
->bHeight
;
163 maxcolors
= entry
->bColorCount
;
165 if (bestEntry
) return bestEntry
;
167 /* Now find a larger one with more colors */
169 maxcolors
= maxwidth
= maxheight
= 255;
170 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
171 if ((entry
->bWidth
<= maxwidth
) && (entry
->bHeight
<= maxheight
) &&
172 (entry
->bColorCount
> colors
) && (entry
->bColorCount
<= maxcolors
))
175 maxwidth
= entry
->bWidth
;
176 maxheight
= entry
->bHeight
;
177 maxcolors
= entry
->bColorCount
;
184 /**********************************************************************
185 * CURSORICON32_FindBestCursor
187 * Find the cursor closest to the requested size.
189 static CURSORDIRENTRY32
*CURSORICON32_FindBestCursor( CURSORICONDIR32
*dir
,
190 int width
, int height
)
192 int i
, maxwidth
, maxheight
;
193 CURSORDIRENTRY32
*entry
, *bestEntry
= NULL
;
195 if (dir
->idCount
< 1)
197 fprintf( stderr
, "Cursor: empty directory!\n" );
200 if (dir
->idCount
== 1) return &dir
->idEntries
[0].cursor
; /* No choice... */
202 /* First find the largest one smaller than or equal to the requested size*/
204 maxwidth
= maxheight
= 0;
205 for(i
= 0,entry
= &dir
->idEntries
[0].cursor
; i
< dir
->idCount
; i
++,entry
++)
206 if ((entry
->wWidth
<= width
) && (entry
->wHeight
<= height
) &&
207 (entry
->wWidth
> maxwidth
) && (entry
->wHeight
> maxheight
))
210 maxwidth
= entry
->wWidth
;
211 maxheight
= entry
->wHeight
;
213 if (bestEntry
) return bestEntry
;
215 /* Now find the smallest one larger than the requested size */
217 maxwidth
= maxheight
= 255;
218 for(i
= 0,entry
= &dir
->idEntries
[0].cursor
; i
< dir
->idCount
; i
++,entry
++)
219 if ((entry
->wWidth
< maxwidth
) && (entry
->wHeight
< maxheight
))
222 maxwidth
= entry
->wWidth
;
223 maxheight
= entry
->wHeight
;
230 /**********************************************************************
231 * CURSORICON32_LoadDirEntry
233 * Load the icon/cursor directory for a given resource name and find the
234 * best matching entry.
236 static BOOL
CURSORICON32_LoadDirEntry(HANDLE hInstance
, LPCWSTR name
,
237 int width
, int height
, int colors
,
238 BOOL fCursor
, CURSORICONDIRENTRY32
*dirEntry
)
242 CURSORICONDIR32
*dir
;
243 CURSORICONDIRENTRY32
*entry
= NULL
;
245 if (!(hRsrc
= FindResource32W( hInstance
, name
,
246 (LPCWSTR
)(fCursor
? RT_GROUP_CURSOR
: RT_GROUP_ICON
) )))
248 if (!(hMem
= LoadResource32( hInstance
, hRsrc
))) return FALSE
;
249 if ((dir
= (CURSORICONDIR32
*)LockResource32( hMem
)))
252 entry
= (CURSORICONDIRENTRY32
*)CURSORICON32_FindBestCursor( dir
,
255 entry
= (CURSORICONDIRENTRY32
*)CURSORICON32_FindBestIcon( dir
,
256 width
, height
, colors
);
257 if (entry
) *dirEntry
= *entry
;
259 FreeResource32( hMem
);
260 return (entry
!= NULL
);
264 /**********************************************************************
265 * CURSORICON32_LoadHandler
267 * Create a cursor or icon from a resource.
269 static HANDLE
CURSORICON32_LoadHandler( HANDLE32 handle
, HINSTANCE hInstance
,
272 HANDLE hAndBits
, hXorBits
, hRes
;
274 int size
, sizeAnd
, sizeXor
;
275 POINT16 hotspot
= { 0 ,0 };
276 BITMAPOBJ
*bmpXor
, *bmpAnd
;
277 BITMAPINFO
*bmi
, *pInfo
;
278 CURSORICONINFO
*info
;
282 if (fCursor
) /* If cursor, get the hotspot */
284 POINT16
*pt
= (POINT16
*)LockResource32( handle
);
286 bmi
= (BITMAPINFO
*)(pt
+ 1);
288 else bmi
= (BITMAPINFO
*)LockResource32( handle
);
290 /* Create a copy of the bitmap header */
292 size
= DIB_BitmapInfoSize( bmi
, DIB_RGB_COLORS
);
293 /* Make sure we have room for the monochrome bitmap later on */
294 size
= MAX( size
, sizeof(BITMAPINFOHEADER
) + 2*sizeof(RGBQUAD
) );
295 pInfo
= (BITMAPINFO
*)xmalloc( size
);
296 memcpy( pInfo
, bmi
, size
);
298 if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPINFOHEADER
))
300 if (pInfo
->bmiHeader
.biCompression
!= BI_RGB
)
302 fprintf(stderr
,"Unknown size for compressed icon bitmap.\n");
306 pInfo
->bmiHeader
.biHeight
/= 2;
308 else if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPCOREHEADER
))
310 BITMAPCOREHEADER
*core
= (BITMAPCOREHEADER
*)pInfo
;
315 fprintf( stderr
, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
316 pInfo
->bmiHeader
.biSize
);
321 /* Create the XOR bitmap */
323 if (!(hdc
= GetDC( 0 )))
329 hXorBits
= CreateDIBitmap( hdc
, &pInfo
->bmiHeader
, CBM_INIT
,
330 (char*)bmi
+ size
, pInfo
, DIB_RGB_COLORS
);
332 /* Fix the bitmap header to load the monochrome mask */
334 if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPINFOHEADER
))
336 BITMAPINFOHEADER
*bih
= &pInfo
->bmiHeader
;
337 RGBQUAD
*rgb
= pInfo
->bmiColors
;
338 bits
= (char *)bmi
+ size
+
339 DIB_GetImageWidthBytes(bih
->biWidth
,bih
->biBitCount
)*bih
->biHeight
;
341 bih
->biClrUsed
= bih
->biClrImportant
= 2;
342 rgb
[0].rgbBlue
= rgb
[0].rgbGreen
= rgb
[0].rgbRed
= 0x00;
343 rgb
[1].rgbBlue
= rgb
[1].rgbGreen
= rgb
[1].rgbRed
= 0xff;
344 rgb
[0].rgbReserved
= rgb
[1].rgbReserved
= 0;
348 BITMAPCOREHEADER
*bch
= (BITMAPCOREHEADER
*)pInfo
;
349 RGBTRIPLE
*rgb
= (RGBTRIPLE
*)(bch
+ 1);
350 bits
= (char *)bmi
+ size
+
351 DIB_GetImageWidthBytes(bch
->bcWidth
,bch
->bcBitCount
)*bch
->bcHeight
;
353 rgb
[0].rgbtBlue
= rgb
[0].rgbtGreen
= rgb
[0].rgbtRed
= 0x00;
354 rgb
[1].rgbtBlue
= rgb
[1].rgbtGreen
= rgb
[1].rgbtRed
= 0xff;
357 /* Create the AND bitmap */
359 hAndBits
= CreateDIBitmap( hdc
, &pInfo
->bmiHeader
, CBM_INIT
,
360 bits
, pInfo
, DIB_RGB_COLORS
);
363 /* Now create the CURSORICONINFO structure */
365 bmpXor
= (BITMAPOBJ
*) GDI_GetObjPtr( hXorBits
, BITMAP_MAGIC
);
366 bmpAnd
= (BITMAPOBJ
*) GDI_GetObjPtr( hAndBits
, BITMAP_MAGIC
);
367 sizeXor
= bmpXor
->bitmap
.bmHeight
* bmpXor
->bitmap
.bmWidthBytes
;
368 sizeAnd
= bmpAnd
->bitmap
.bmHeight
* bmpAnd
->bitmap
.bmWidthBytes
;
370 if (!(hRes
= GlobalAlloc16( GMEM_MOVEABLE
,
371 sizeof(CURSORICONINFO
) + sizeXor
+ sizeAnd
)))
373 DeleteObject( hXorBits
);
374 DeleteObject( hAndBits
);
378 /* Make it owned by the module */
379 if (hInstance
) FarSetOwner( hRes
, (WORD
)(DWORD
)GetExePtr(hInstance
) );
381 info
= (CURSORICONINFO
*)GlobalLock16( hRes
);
382 info
->ptHotSpot
.x
= hotspot
.x
;
383 info
->ptHotSpot
.y
= hotspot
.y
;
384 info
->nWidth
= bmpXor
->bitmap
.bmWidth
;
385 info
->nHeight
= bmpXor
->bitmap
.bmHeight
;
386 info
->nWidthBytes
= bmpXor
->bitmap
.bmWidthBytes
;
387 info
->bPlanes
= bmpXor
->bitmap
.bmPlanes
;
388 info
->bBitsPerPixel
= bmpXor
->bitmap
.bmBitsPixel
;
390 /* Transfer the bitmap bits to the CURSORICONINFO structure */
392 GetBitmapBits( hAndBits
, sizeAnd
, (char *)(info
+ 1) );
393 GetBitmapBits( hXorBits
, sizeXor
, (char *)(info
+ 1) + sizeAnd
);
394 DeleteObject( hXorBits
);
395 DeleteObject( hAndBits
);
396 GlobalUnlock16( hRes
);
400 /**********************************************************************
403 * Load a cursor or icon.
405 static HANDLE
CURSORICON32_Load( HANDLE hInstance
, LPCWSTR name
, int width
,
406 int height
, int colors
, BOOL fCursor
)
411 CURSORICONDIRENTRY32 dirEntry
;
413 if(!hInstance
) /* OEM cursor/icon */
419 ansi
=STRING32_DupUniToAnsi(name
);
420 if(ansi
[0]=='#') /*Check for '#xxx' name */
430 resid
=(WORD
)(int)name
;
431 return OBM_LoadCursorIcon(resid
, fCursor
);
434 /* Find the best entry in the directory */
436 if (!CURSORICON32_LoadDirEntry( hInstance
, name
, width
, height
,
437 colors
, fCursor
, &dirEntry
)) return 0;
439 /* Load the resource */
441 if (!(hRsrc
= FindResource32W( hInstance
,
442 (LPWSTR
) (DWORD
) dirEntry
.icon
.wResId
,
443 (LPWSTR
) (fCursor
? RT_CURSOR
: RT_ICON
)))) return 0;
444 if (!(handle
= LoadResource32( hInstance
, hRsrc
))) return 0;
446 /* Use the resource handle as key to detect multiple loading */
447 if(CURSORICON_lookup(handle
,&hRet
))
450 hRet
= CURSORICON32_LoadHandler( handle
, hInstance
, fCursor
);
451 /* Obsolete - FreeResource32(handle);*/
452 CURSORICON_insert(handle
,hRet
);
457 /***********************************************************************
458 * LoadCursorW (USER32.361)
460 HCURSOR32
LoadCursor32W(HINSTANCE32 hInstance
,LPCWSTR name
)
462 return CURSORICON32_Load( hInstance
, name
,
463 SYSMETRICS_CXCURSOR
, SYSMETRICS_CYCURSOR
, 1, TRUE
);
466 /***********************************************************************
467 * LoadCursorA (USER32.358)
469 HCURSOR32
LoadCursor32A(HINSTANCE32 hInstance
,LPCSTR name
)
473 return LoadCursor32W(hInstance
,(LPCWSTR
)name
);
475 LPWSTR uni
= STRING32_DupAnsiToUni(name
);
476 res
= LoadCursor32W(hInstance
, uni
);
483 /***********************************************************************
484 * LoadIconW (USER32.363)
486 HICON32
LoadIcon32W(HINSTANCE32 hInstance
,LPCWSTR name
)
488 return CURSORICON32_Load( hInstance
, name
,
489 SYSMETRICS_CXICON
, SYSMETRICS_CYICON
,
490 MIN( 16, 1 << screenDepth
), FALSE
);
493 /***********************************************************************
494 * LoadIconA (USER32.362)
496 HICON32
LoadIcon32A(HINSTANCE32 hInstance
,LPCSTR name
)
500 return LoadIcon32W(hInstance
, (LPCWSTR
)name
);
502 LPWSTR uni
= STRING32_DupAnsiToUni(name
);
503 res
= LoadIcon32W(hInstance
, uni
);