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"
36 #include "resource32.h"
42 /* This dictionary could might eventually become a macro for better reuse */
43 struct MAP_DWORD_DWORD
{
48 struct MAP_DWORD_DWORD
*CURSORICON_map
;
51 BOOL
CURSORICON_lookup(DWORD key
,DWORD
*value
)
54 for(i
=0;i
<CURSORICON_count
;i
++)
56 if(key
==CURSORICON_map
[i
].key
)
58 *value
=CURSORICON_map
[i
].value
;
65 void CURSORICON_insert(DWORD key
,DWORD value
)
70 CURSORICON_map
=malloc(sizeof(struct MAP_DWORD_DWORD
));
73 CURSORICON_map
=realloc(CURSORICON_map
,
74 sizeof(struct MAP_DWORD_DWORD
)*CURSORICON_count
);
76 CURSORICON_map
[CURSORICON_count
-1].key
=key
;
77 CURSORICON_map
[CURSORICON_count
-1].value
=value
;
80 /**********************************************************************
81 * CURSORICON32_FindBestIcon
83 * Find the icon closest to the requested size and number of colors.
85 static ICONDIRENTRY32
*CURSORICON32_FindBestIcon( CURSORICONDIR32
*dir
,
86 int width
, int height
, int colors
)
88 int i
, maxcolors
, maxwidth
, maxheight
;
89 ICONDIRENTRY32
*entry
, *bestEntry
= NULL
;
93 fprintf( stderr
, "Icon: empty directory!\n" );
96 if (dir
->idCount
== 1) return &dir
->idEntries
[0].icon
; /* No choice... */
98 /* First find the exact size with less colors */
101 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
102 if ((entry
->bWidth
== width
) && (entry
->bHeight
== height
) &&
103 (entry
->bColorCount
<= colors
) && (entry
->bColorCount
> maxcolors
))
106 maxcolors
= entry
->bColorCount
;
108 if (bestEntry
) return bestEntry
;
110 /* First find the exact size with more colors */
113 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
114 if ((entry
->bWidth
== width
) && (entry
->bHeight
== height
) &&
115 (entry
->bColorCount
> colors
) && (entry
->bColorCount
<= maxcolors
))
118 maxcolors
= entry
->bColorCount
;
120 if (bestEntry
) return bestEntry
;
122 /* Now find a smaller one with less colors */
124 maxcolors
= maxwidth
= maxheight
= 0;
125 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
126 if ((entry
->bWidth
<= width
) && (entry
->bHeight
<= height
) &&
127 (entry
->bWidth
>= maxwidth
) && (entry
->bHeight
>= maxheight
) &&
128 (entry
->bColorCount
<= colors
) && (entry
->bColorCount
> maxcolors
))
131 maxwidth
= entry
->bWidth
;
132 maxheight
= entry
->bHeight
;
133 maxcolors
= entry
->bColorCount
;
135 if (bestEntry
) return bestEntry
;
137 /* Now find a smaller one with more colors */
140 maxwidth
= maxheight
= 0;
141 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
142 if ((entry
->bWidth
<= width
) && (entry
->bHeight
<= height
) &&
143 (entry
->bWidth
>= maxwidth
) && (entry
->bHeight
>= maxheight
) &&
144 (entry
->bColorCount
> colors
) && (entry
->bColorCount
<= maxcolors
))
147 maxwidth
= entry
->bWidth
;
148 maxheight
= entry
->bHeight
;
149 maxcolors
= entry
->bColorCount
;
151 if (bestEntry
) return bestEntry
;
153 /* Now find a larger one with less colors */
156 maxwidth
= maxheight
= 255;
157 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
158 if ((entry
->bWidth
<= maxwidth
) && (entry
->bHeight
<= maxheight
) &&
159 (entry
->bColorCount
<= colors
) && (entry
->bColorCount
> maxcolors
))
162 maxwidth
= entry
->bWidth
;
163 maxheight
= entry
->bHeight
;
164 maxcolors
= entry
->bColorCount
;
166 if (bestEntry
) return bestEntry
;
168 /* Now find a larger one with more colors */
170 maxcolors
= maxwidth
= maxheight
= 255;
171 for (i
= 0, entry
= &dir
->idEntries
[0].icon
; i
< dir
->idCount
; i
++,entry
++)
172 if ((entry
->bWidth
<= maxwidth
) && (entry
->bHeight
<= maxheight
) &&
173 (entry
->bColorCount
> colors
) && (entry
->bColorCount
<= maxcolors
))
176 maxwidth
= entry
->bWidth
;
177 maxheight
= entry
->bHeight
;
178 maxcolors
= entry
->bColorCount
;
185 /**********************************************************************
186 * CURSORICON32_FindBestCursor
188 * Find the cursor closest to the requested size.
190 static CURSORDIRENTRY32
*CURSORICON32_FindBestCursor( CURSORICONDIR32
*dir
,
191 int width
, int height
)
193 int i
, maxwidth
, maxheight
;
194 CURSORDIRENTRY32
*entry
, *bestEntry
= NULL
;
196 if (dir
->idCount
< 1)
198 fprintf( stderr
, "Cursor: empty directory!\n" );
201 if (dir
->idCount
== 1) return &dir
->idEntries
[0].cursor
; /* No choice... */
203 /* First find the largest one smaller than or equal to the requested size*/
205 maxwidth
= maxheight
= 0;
206 for(i
= 0,entry
= &dir
->idEntries
[0].cursor
; i
< dir
->idCount
; i
++,entry
++)
207 if ((entry
->wWidth
<= width
) && (entry
->wHeight
<= height
) &&
208 (entry
->wWidth
> maxwidth
) && (entry
->wHeight
> maxheight
))
211 maxwidth
= entry
->wWidth
;
212 maxheight
= entry
->wHeight
;
214 if (bestEntry
) return bestEntry
;
216 /* Now find the smallest one larger than the requested size */
218 maxwidth
= maxheight
= 255;
219 for(i
= 0,entry
= &dir
->idEntries
[0].cursor
; i
< dir
->idCount
; i
++,entry
++)
220 if ((entry
->wWidth
< maxwidth
) && (entry
->wHeight
< maxheight
))
223 maxwidth
= entry
->wWidth
;
224 maxheight
= entry
->wHeight
;
231 /**********************************************************************
232 * CURSORICON32_LoadDirEntry
234 * Load the icon/cursor directory for a given resource name and find the
235 * best matching entry.
237 static BOOL
CURSORICON32_LoadDirEntry(HANDLE hInstance
, LPCWSTR name
,
238 int width
, int height
, int colors
,
239 BOOL fCursor
, CURSORICONDIRENTRY32
*dirEntry
)
243 CURSORICONDIR32
*dir
;
244 CURSORICONDIRENTRY32
*entry
= NULL
;
246 if (!(hRsrc
= FindResource32( hInstance
, name
,
247 (LPCWSTR
)(fCursor
? RT_GROUP_CURSOR
: RT_GROUP_ICON
) )))
249 if (!(hMem
= LoadResource32( hInstance
, hRsrc
))) return FALSE
;
250 if ((dir
= (CURSORICONDIR32
*)LockResource32( hMem
)))
253 entry
= (CURSORICONDIRENTRY32
*)CURSORICON32_FindBestCursor( dir
,
256 entry
= (CURSORICONDIRENTRY32
*)CURSORICON32_FindBestIcon( dir
,
257 width
, height
, colors
);
258 if (entry
) *dirEntry
= *entry
;
260 FreeResource32( hMem
);
261 return (entry
!= NULL
);
265 /**********************************************************************
266 * CURSORICON32_LoadHandler
268 * Create a cursor or icon from a resource.
270 static HANDLE
CURSORICON32_LoadHandler( HANDLE32 handle
, HINSTANCE hInstance
,
273 HANDLE hAndBits
, hXorBits
, hRes
;
275 int size
, sizeAnd
, sizeXor
;
276 POINT16 hotspot
= { 0 ,0 };
277 BITMAPOBJ
*bmpXor
, *bmpAnd
;
278 BITMAPINFO
*bmi
, *pInfo
;
279 CURSORICONINFO
*info
;
283 if (fCursor
) /* If cursor, get the hotspot */
285 POINT16
*pt
= (POINT16
*)LockResource32( handle
);
287 bmi
= (BITMAPINFO
*)(pt
+ 1);
289 else bmi
= (BITMAPINFO
*)LockResource32( handle
);
291 /* Create a copy of the bitmap header */
293 size
= DIB_BitmapInfoSize( bmi
, DIB_RGB_COLORS
);
294 /* Make sure we have room for the monochrome bitmap later on */
295 size
= MAX( size
, sizeof(BITMAPINFOHEADER
) + 2*sizeof(RGBQUAD
) );
296 pInfo
= (BITMAPINFO
*)xmalloc( size
);
297 memcpy( pInfo
, bmi
, size
);
299 if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPINFOHEADER
))
301 if (pInfo
->bmiHeader
.biCompression
!= BI_RGB
)
303 fprintf(stderr
,"Unknown size for compressed icon bitmap.\n");
307 pInfo
->bmiHeader
.biHeight
/= 2;
309 else if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPCOREHEADER
))
311 BITMAPCOREHEADER
*core
= (BITMAPCOREHEADER
*)pInfo
;
316 fprintf( stderr
, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
317 pInfo
->bmiHeader
.biSize
);
322 /* Create the XOR bitmap */
324 if (!(hdc
= GetDC( 0 )))
330 hXorBits
= CreateDIBitmap( hdc
, &pInfo
->bmiHeader
, CBM_INIT
,
331 (char*)bmi
+ size
, pInfo
, DIB_RGB_COLORS
);
333 /* Fix the bitmap header to load the monochrome mask */
335 if (pInfo
->bmiHeader
.biSize
== sizeof(BITMAPINFOHEADER
))
337 BITMAPINFOHEADER
*bih
= &pInfo
->bmiHeader
;
338 RGBQUAD
*rgb
= pInfo
->bmiColors
;
339 bits
= (char *)bmi
+ size
+
340 DIB_GetImageWidthBytes(bih
->biWidth
,bih
->biBitCount
)*bih
->biHeight
;
342 bih
->biClrUsed
= bih
->biClrImportant
= 2;
343 rgb
[0].rgbBlue
= rgb
[0].rgbGreen
= rgb
[0].rgbRed
= 0x00;
344 rgb
[1].rgbBlue
= rgb
[1].rgbGreen
= rgb
[1].rgbRed
= 0xff;
345 rgb
[0].rgbReserved
= rgb
[1].rgbReserved
= 0;
349 BITMAPCOREHEADER
*bch
= (BITMAPCOREHEADER
*)pInfo
;
350 RGBTRIPLE
*rgb
= (RGBTRIPLE
*)(bch
+ 1);
351 bits
= (char *)bmi
+ size
+
352 DIB_GetImageWidthBytes(bch
->bcWidth
,bch
->bcBitCount
)*bch
->bcHeight
;
354 rgb
[0].rgbtBlue
= rgb
[0].rgbtGreen
= rgb
[0].rgbtRed
= 0x00;
355 rgb
[1].rgbtBlue
= rgb
[1].rgbtGreen
= rgb
[1].rgbtRed
= 0xff;
358 /* Create the AND bitmap */
360 hAndBits
= CreateDIBitmap( hdc
, &pInfo
->bmiHeader
, CBM_INIT
,
361 bits
, pInfo
, DIB_RGB_COLORS
);
364 /* Now create the CURSORICONINFO structure */
366 bmpXor
= (BITMAPOBJ
*) GDI_GetObjPtr( hXorBits
, BITMAP_MAGIC
);
367 bmpAnd
= (BITMAPOBJ
*) GDI_GetObjPtr( hAndBits
, BITMAP_MAGIC
);
368 sizeXor
= bmpXor
->bitmap
.bmHeight
* bmpXor
->bitmap
.bmWidthBytes
;
369 sizeAnd
= bmpAnd
->bitmap
.bmHeight
* bmpAnd
->bitmap
.bmWidthBytes
;
371 if (!(hRes
= GlobalAlloc16( GMEM_MOVEABLE
,
372 sizeof(CURSORICONINFO
) + sizeXor
+ sizeAnd
)))
374 DeleteObject( hXorBits
);
375 DeleteObject( hAndBits
);
379 /* Make it owned by the module */
380 if (hInstance
) FarSetOwner( hRes
, (WORD
)(DWORD
)GetExePtr(hInstance
) );
382 info
= (CURSORICONINFO
*)GlobalLock16( hRes
);
383 info
->ptHotSpot
.x
= hotspot
.x
;
384 info
->ptHotSpot
.y
= hotspot
.y
;
385 info
->nWidth
= bmpXor
->bitmap
.bmWidth
;
386 info
->nHeight
= bmpXor
->bitmap
.bmHeight
;
387 info
->nWidthBytes
= bmpXor
->bitmap
.bmWidthBytes
;
388 info
->bPlanes
= bmpXor
->bitmap
.bmPlanes
;
389 info
->bBitsPerPixel
= bmpXor
->bitmap
.bmBitsPixel
;
391 /* Transfer the bitmap bits to the CURSORICONINFO structure */
393 GetBitmapBits( hAndBits
, sizeAnd
, (char *)(info
+ 1) );
394 GetBitmapBits( hXorBits
, sizeXor
, (char *)(info
+ 1) + sizeAnd
);
395 DeleteObject( hXorBits
);
396 DeleteObject( hAndBits
);
397 GlobalUnlock16( hRes
);
401 /**********************************************************************
404 * Load a cursor or icon.
406 static HANDLE
CURSORICON32_Load( HANDLE hInstance
, LPCWSTR name
, int width
,
407 int height
, int colors
, BOOL fCursor
)
412 CURSORICONDIRENTRY32 dirEntry
;
414 if(!hInstance
) /* OEM cursor/icon */
420 ansi
=STRING32_DupUniToAnsi(name
);
421 if(ansi
[0]=='#') /*Check for '#xxx' name */
431 resid
=(WORD
)(int)name
;
432 return OBM_LoadCursorIcon(resid
, fCursor
);
435 /* Find the best entry in the directory */
437 if (!CURSORICON32_LoadDirEntry( hInstance
, name
, width
, height
,
438 colors
, fCursor
, &dirEntry
)) return 0;
440 /* Load the resource */
442 if (!(hRsrc
= FindResource32( hInstance
,
443 (LPWSTR
) (DWORD
) dirEntry
.icon
.wResId
,
444 (LPWSTR
) (fCursor
? RT_CURSOR
: RT_ICON
)))) return 0;
445 if (!(handle
= LoadResource32( hInstance
, hRsrc
))) return 0;
447 /* Use the resource handle as key to detect multiple loading */
448 if(CURSORICON_lookup(handle
,&hRet
))
451 hRet
= CURSORICON32_LoadHandler( handle
, hInstance
, fCursor
);
452 /* Obsolete - FreeResource32(handle);*/
453 CURSORICON_insert(handle
,hRet
);
458 /***********************************************************************
461 HCURSOR
WIN32_LoadCursorW( HANDLE hInstance
, LPCWSTR name
)
463 return CURSORICON32_Load( hInstance
, name
,
464 SYSMETRICS_CXCURSOR
, SYSMETRICS_CYCURSOR
, 1, TRUE
);
467 HCURSOR
WIN32_LoadCursorA(HANDLE hInstance
, LPCSTR name
)
471 return WIN32_LoadCursorW(hInstance
, name
);
473 LPWSTR uni
= STRING32_DupAnsiToUni(name
);
474 res
= WIN32_LoadCursorW(hInstance
, uni
);
481 /***********************************************************************
484 HICON
WIN32_LoadIconW( HANDLE hInstance
, LPCWSTR name
)
486 return CURSORICON32_Load( hInstance
, name
,
487 SYSMETRICS_CXICON
, SYSMETRICS_CYICON
,
488 MIN( 16, 1 << screenDepth
), FALSE
);
491 HICON
WIN32_LoadIconA( HANDLE hInstance
, LPCSTR name
)
495 return WIN32_LoadIconW(hInstance
, name
);
497 LPWSTR uni
= STRING32_DupAnsiToUni(name
);
498 res
= WIN32_LoadIconW(hInstance
, uni
);