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 :-(
30 #include "cursoricon.h"
31 #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(HINSTANCE32 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 HGLOBAL32
CURSORICON32_LoadHandler( HANDLE32 handle
,
270 HINSTANCE32 hInstance
,
273 HBITMAP32 hAndBits
, hXorBits
;
276 int size
, sizeAnd
, sizeXor
;
277 POINT16 hotspot
= { 0 ,0 };
278 BITMAPOBJ
*bmpXor
, *bmpAnd
;
279 BITMAPINFO
*bmi
, *pInfo
;
280 CURSORICONINFO
*info
;
284 if (fCursor
) /* If cursor, get the hotspot */
286 POINT16
*pt
= (POINT16
*)LockResource32( handle
);
288 bmi
= (BITMAPINFO
*)(pt
+ 1);
290 else bmi
= (BITMAPINFO
*)LockResource32( handle
);
292 /* Create a copy of the bitmap header */
294 size
= DIB_BitmapInfoSize( bmi
, DIB_RGB_COLORS
);
295 /* Make sure we have room for the monochrome bitmap later on */
296 size
= MAX( size
, sizeof(BITMAPINFOHEADER
) + 2*sizeof(RGBQUAD
) );
297 pInfo
= (BITMAPINFO
*)xmalloc( size
);
298 memcpy( pInfo
, bmi
, size
);
300 if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPINFOHEADER
))
302 if (pInfo
->bmiHeader
.biCompression
!= BI_RGB
)
304 fprintf(stderr
,"Unknown size for compressed icon bitmap.\n");
308 pInfo
->bmiHeader
.biHeight
/= 2;
310 else if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPCOREHEADER
))
312 BITMAPCOREHEADER
*core
= (BITMAPCOREHEADER
*)pInfo
;
317 fprintf( stderr
, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
318 pInfo
->bmiHeader
.biSize
);
323 /* Create the XOR bitmap */
325 if (!(hdc
= GetDC32( 0 )))
331 hXorBits
= CreateDIBitmap( hdc
, &pInfo
->bmiHeader
, CBM_INIT
,
332 (char*)bmi
+ size
, pInfo
, DIB_RGB_COLORS
);
334 /* Fix the bitmap header to load the monochrome mask */
336 if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPINFOHEADER
))
338 BITMAPINFOHEADER
*bih
= &pInfo
->bmiHeader
;
339 RGBQUAD
*rgb
= pInfo
->bmiColors
;
340 bits
= (char *)bmi
+ size
+
341 DIB_GetImageWidthBytes(bih
->biWidth
,bih
->biBitCount
)*bih
->biHeight
;
343 bih
->biClrUsed
= bih
->biClrImportant
= 2;
344 rgb
[0].rgbBlue
= rgb
[0].rgbGreen
= rgb
[0].rgbRed
= 0x00;
345 rgb
[1].rgbBlue
= rgb
[1].rgbGreen
= rgb
[1].rgbRed
= 0xff;
346 rgb
[0].rgbReserved
= rgb
[1].rgbReserved
= 0;
350 BITMAPCOREHEADER
*bch
= (BITMAPCOREHEADER
*)pInfo
;
351 RGBTRIPLE
*rgb
= (RGBTRIPLE
*)(bch
+ 1);
352 bits
= (char *)bmi
+ size
+
353 DIB_GetImageWidthBytes(bch
->bcWidth
,bch
->bcBitCount
)*bch
->bcHeight
;
355 rgb
[0].rgbtBlue
= rgb
[0].rgbtGreen
= rgb
[0].rgbtRed
= 0x00;
356 rgb
[1].rgbtBlue
= rgb
[1].rgbtGreen
= rgb
[1].rgbtRed
= 0xff;
359 /* Create the AND bitmap */
361 hAndBits
= CreateDIBitmap( hdc
, &pInfo
->bmiHeader
, CBM_INIT
,
362 bits
, pInfo
, DIB_RGB_COLORS
);
363 ReleaseDC32( 0, hdc
);
365 /* Now create the CURSORICONINFO structure */
367 bmpXor
= (BITMAPOBJ
*) GDI_GetObjPtr( hXorBits
, BITMAP_MAGIC
);
368 bmpAnd
= (BITMAPOBJ
*) GDI_GetObjPtr( hAndBits
, BITMAP_MAGIC
);
369 sizeXor
= bmpXor
->bitmap
.bmHeight
* bmpXor
->bitmap
.bmWidthBytes
;
370 sizeAnd
= bmpAnd
->bitmap
.bmHeight
* bmpAnd
->bitmap
.bmWidthBytes
;
372 if (!(hRes
= GlobalAlloc16( GMEM_MOVEABLE
,
373 sizeof(CURSORICONINFO
) + sizeXor
+ sizeAnd
)))
375 DeleteObject32( hXorBits
);
376 DeleteObject32( hAndBits
);
380 /* Make it owned by the module */
381 if (hInstance
) FarSetOwner( hRes
, (WORD
)(DWORD
)GetExePtr(hInstance
) );
383 info
= (CURSORICONINFO
*)GlobalLock16( hRes
);
384 info
->ptHotSpot
.x
= hotspot
.x
;
385 info
->ptHotSpot
.y
= hotspot
.y
;
386 info
->nWidth
= bmpXor
->bitmap
.bmWidth
;
387 info
->nHeight
= bmpXor
->bitmap
.bmHeight
;
388 info
->nWidthBytes
= bmpXor
->bitmap
.bmWidthBytes
;
389 info
->bPlanes
= bmpXor
->bitmap
.bmPlanes
;
390 info
->bBitsPerPixel
= bmpXor
->bitmap
.bmBitsPixel
;
392 /* Transfer the bitmap bits to the CURSORICONINFO structure */
394 GetBitmapBits( hAndBits
, sizeAnd
, (char *)(info
+ 1) );
395 GetBitmapBits( hXorBits
, sizeXor
, (char *)(info
+ 1) + sizeAnd
);
396 DeleteObject32( hXorBits
);
397 DeleteObject32( hAndBits
);
398 GlobalUnlock16( hRes
);
402 /**********************************************************************
405 * Load a cursor or icon.
407 static HGLOBAL32
CURSORICON32_Load( HINSTANCE32 hInstance
, LPCWSTR name
,
408 int width
, int height
, int colors
,
414 CURSORICONDIRENTRY32 dirEntry
;
416 if(!hInstance
) /* OEM cursor/icon */
422 ansi
=STRING32_DupUniToAnsi(name
);
423 if(ansi
[0]=='#') /*Check for '#xxx' name */
433 resid
=(WORD
)(int)name
;
434 return OBM_LoadCursorIcon(resid
, fCursor
);
437 /* Find the best entry in the directory */
439 if (!CURSORICON32_LoadDirEntry( hInstance
, name
, width
, height
,
440 colors
, fCursor
, &dirEntry
)) return 0;
442 /* Load the resource */
444 if (!(hRsrc
= FindResource32W( hInstance
,
445 (LPWSTR
) (DWORD
) dirEntry
.icon
.wResId
,
446 (LPWSTR
) (fCursor
? RT_CURSOR
: RT_ICON
)))) return 0;
447 if (!(handle
= LoadResource32( hInstance
, hRsrc
))) return 0;
449 /* Use the resource handle as key to detect multiple loading */
450 if(CURSORICON_lookup(handle
,&hRet
))
453 hRet
= CURSORICON32_LoadHandler( handle
, hInstance
, fCursor
);
454 /* Obsolete - FreeResource32(handle);*/
455 CURSORICON_insert(handle
,hRet
);
460 /***********************************************************************
461 * LoadCursorW (USER32.361)
463 HCURSOR32
LoadCursor32W(HINSTANCE32 hInstance
,LPCWSTR name
)
465 return CURSORICON32_Load( hInstance
, name
,
466 SYSMETRICS_CXCURSOR
, SYSMETRICS_CYCURSOR
, 1, TRUE
);
469 /***********************************************************************
470 * LoadCursorA (USER32.358)
472 HCURSOR32
LoadCursor32A(HINSTANCE32 hInstance
,LPCSTR name
)
476 return LoadCursor32W(hInstance
,(LPCWSTR
)name
);
478 LPWSTR uni
= STRING32_DupAnsiToUni(name
);
479 res
= LoadCursor32W(hInstance
, uni
);
486 /***********************************************************************
487 * LoadIconW (USER32.363)
489 HICON32
LoadIcon32W(HINSTANCE32 hInstance
,LPCWSTR name
)
491 return CURSORICON32_Load( hInstance
, name
,
492 SYSMETRICS_CXICON
, SYSMETRICS_CYICON
,
493 MIN( 16, 1 << screenDepth
), FALSE
);
496 /***********************************************************************
497 * LoadIconA (USER32.362)
499 HICON32
LoadIcon32A(HINSTANCE32 hInstance
,LPCSTR name
)
503 return LoadIcon32W(hInstance
, (LPCWSTR
)name
);
505 LPWSTR uni
= STRING32_DupAnsiToUni(name
);
506 res
= LoadIcon32W(hInstance
, uni
);