Release 960331
[wine] / objects / cursoricon.c
1 /*
2  * Cursor and icon support
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 /*
8  * Theory:
9  *
10  * Cursors and icons are stored in a global heap block, with the
11  * following layout:
12  *
13  * CURSORICONINFO info;
14  * BYTE[]         ANDbits;
15  * BYTE[]         XORbits;
16  *
17  * The bits structures are in the format of a device-dependent bitmap.
18  *
19  * This layout is very sub-optimal, as the bitmap bits are stored in
20  * the X client instead of in the server like other bitmaps; however,
21  * some programs (notably Paint Brush) expect to be able to manipulate
22  * the bits directly :-(
23  */
24
25 #include <string.h>
26 #include <stdlib.h>
27 #include "windows.h"
28 #include "bitmap.h"
29 #include "callback.h"
30 #include "cursoricon.h"
31 #include "sysmetrics.h"
32 #include "win.h"
33 #include "stddebug.h"
34 #include "debug.h"
35 #include "xmalloc.h"
36 #include "task.h"
37
38
39 Cursor CURSORICON_XCursor = None;  /* Current X cursor */
40 static HCURSOR hActiveCursor = 0;  /* Active cursor */
41 static int CURSOR_ShowCount = 0;   /* Cursor display count */
42 static RECT CURSOR_ClipRect;       /* Cursor clipping rect */
43
44 /**********************************************************************
45  *          CURSORICON_FindBestIcon
46  *
47  * Find the icon closest to the requested size and number of colors.
48  */
49 static ICONDIRENTRY *CURSORICON_FindBestIcon( CURSORICONDIR *dir, int width,
50                                               int height, int colors )
51 {
52     int i, maxcolors, maxwidth, maxheight;
53     ICONDIRENTRY *entry, *bestEntry = NULL;
54
55     if (dir->idCount < 1)
56     {
57         fprintf( stderr, "Icon: empty directory!\n" );
58         return NULL;
59     }
60     if (dir->idCount == 1) return &dir->idEntries[0].icon;  /* No choice... */
61
62     /* First find the exact size with less colors */
63
64     maxcolors = 0;
65     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
66         if ((entry->bWidth == width) && (entry->bHeight == height) &&
67             (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
68         {
69             bestEntry = entry;
70             maxcolors = entry->bColorCount;
71         }
72     if (bestEntry) return bestEntry;
73
74     /* First find the exact size with more colors */
75
76     maxcolors = 255;
77     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
78         if ((entry->bWidth == width) && (entry->bHeight == height) &&
79             (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
80         {
81             bestEntry = entry;
82             maxcolors = entry->bColorCount;
83         }
84     if (bestEntry) return bestEntry;
85
86     /* Now find a smaller one with less colors */
87
88     maxcolors = maxwidth = maxheight = 0;
89     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
90         if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
91             (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
92             (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
93         {
94             bestEntry = entry;
95             maxwidth  = entry->bWidth;
96             maxheight = entry->bHeight;
97             maxcolors = entry->bColorCount;
98         }
99     if (bestEntry) return bestEntry;
100
101     /* Now find a smaller one with more colors */
102
103     maxcolors = 255;
104     maxwidth = maxheight = 0;
105     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
106         if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
107             (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
108             (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
109         {
110             bestEntry = entry;
111             maxwidth  = entry->bWidth;
112             maxheight = entry->bHeight;
113             maxcolors = entry->bColorCount;
114         }
115     if (bestEntry) return bestEntry;
116
117     /* Now find a larger one with less colors */
118
119     maxcolors = 0;
120     maxwidth = maxheight = 255;
121     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
122         if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
123             (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
124         {
125             bestEntry = entry;
126             maxwidth  = entry->bWidth;
127             maxheight = entry->bHeight;
128             maxcolors = entry->bColorCount;
129         }
130     if (bestEntry) return bestEntry;
131
132     /* Now find a larger one with more colors */
133
134     maxcolors = maxwidth = maxheight = 255;
135     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
136         if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
137             (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
138         {
139             bestEntry = entry;
140             maxwidth  = entry->bWidth;
141             maxheight = entry->bHeight;
142             maxcolors = entry->bColorCount;
143         }
144
145     return bestEntry;
146 }
147
148
149 /**********************************************************************
150  *          CURSORICON_FindBestCursor
151  *
152  * Find the cursor closest to the requested size.
153  */
154 static CURSORDIRENTRY *CURSORICON_FindBestCursor( CURSORICONDIR *dir,
155                                                   int width, int height )
156 {
157     int i, maxwidth, maxheight;
158     CURSORDIRENTRY *entry, *bestEntry = NULL;
159
160     if (dir->idCount < 1)
161     {
162         fprintf( stderr, "Cursor: empty directory!\n" );
163         return NULL;
164     }
165     if (dir->idCount == 1) return &dir->idEntries[0].cursor; /* No choice... */
166
167     /* First find the largest one smaller than or equal to the requested size*/
168
169     maxwidth = maxheight = 0;
170     for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
171         if ((entry->wWidth <= width) && (entry->wHeight <= height) &&
172             (entry->wWidth > maxwidth) && (entry->wHeight > maxheight))
173         {
174             bestEntry = entry;
175             maxwidth  = entry->wWidth;
176             maxheight = entry->wHeight;
177         }
178     if (bestEntry) return bestEntry;
179
180     /* Now find the smallest one larger than the requested size */
181
182     maxwidth = maxheight = 255;
183     for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
184         if ((entry->wWidth < maxwidth) && (entry->wHeight < maxheight))
185         {
186             bestEntry = entry;
187             maxwidth  = entry->wWidth;
188             maxheight = entry->wHeight;
189         }
190
191     return bestEntry;
192 }
193
194
195 /**********************************************************************
196  *          CURSORICON_LoadDirEntry
197  *
198  * Load the icon/cursor directory for a given resource name and find the
199  * best matching entry.
200  */
201 static BOOL CURSORICON_LoadDirEntry(HANDLE hInstance, SEGPTR name,
202                                     int width, int height, int colors,
203                                     BOOL fCursor, CURSORICONDIRENTRY *dirEntry)
204 {
205     HRSRC hRsrc;
206     HANDLE hMem;
207     CURSORICONDIR *dir;
208     CURSORICONDIRENTRY *entry = NULL;
209
210     if (!(hRsrc = FindResource( hInstance, name,
211                                 fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON )))
212         return FALSE;
213     if (!(hMem = LoadResource( hInstance, hRsrc ))) return FALSE;
214     if ((dir = (CURSORICONDIR *)LockResource( hMem )))
215     {
216         if (fCursor)
217             entry = (CURSORICONDIRENTRY *)CURSORICON_FindBestCursor( dir,
218                                                                width, height );
219         else
220             entry = (CURSORICONDIRENTRY *)CURSORICON_FindBestIcon( dir,
221                                                        width, height, colors );
222         if (entry) *dirEntry = *entry;
223     }
224     FreeResource( hMem );
225     return (entry != NULL);
226 }
227
228
229 /**********************************************************************
230  *          CURSORICON_LoadHandler 
231  *
232  * Create a cursor or icon from a resource.
233  */
234 static HANDLE CURSORICON_LoadHandler( HANDLE handle, HINSTANCE hInstance,
235                                       BOOL fCursor )
236 {
237     HANDLE hAndBits, hXorBits;
238     HDC hdc;
239     int size, sizeAnd, sizeXor;
240     POINT hotspot = { 0 ,0 };
241     BITMAPOBJ *bmpXor, *bmpAnd;
242     BITMAPINFO *bmi, *pInfo;
243     CURSORICONINFO *info;
244     char *bits;
245
246     if (fCursor)  /* If cursor, get the hotspot */
247     {
248         POINT *pt = (POINT *)LockResource( handle );
249         hotspot = *pt;
250         bmi = (BITMAPINFO *)(pt + 1);
251     }
252     else bmi = (BITMAPINFO *)LockResource( handle );
253
254     /* Create a copy of the bitmap header */
255
256     size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
257     /* Make sure we have room for the monochrome bitmap later on */
258     size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
259     pInfo = (BITMAPINFO *)xmalloc( size );
260     memcpy( pInfo, bmi, size );
261
262     if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
263     {
264         if (pInfo->bmiHeader.biCompression != BI_RGB)
265         {
266             fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
267             free( pInfo );
268             return 0;
269         }
270         pInfo->bmiHeader.biHeight /= 2;
271     }
272     else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
273     {
274         BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
275         core->bcHeight /= 2;
276     }
277     else
278     {
279         fprintf( stderr, "CURSORICON_Load: Unknown bitmap length %ld!\n",
280                  pInfo->bmiHeader.biSize );
281         free( pInfo );
282         return 0;
283     }
284
285     /* Create the XOR bitmap */
286
287     if (!(hdc = GetDC( 0 )))
288     {
289         free( pInfo );
290         return 0;
291     }
292
293     hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
294                                (char*)bmi + size, pInfo, DIB_RGB_COLORS );
295
296     /* Fix the bitmap header to load the monochrome mask */
297
298     if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
299     {
300         BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
301         RGBQUAD *rgb = pInfo->bmiColors;
302         bits = (char *)bmi + size +
303             DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
304         bih->biBitCount = 1;
305         bih->biClrUsed = bih->biClrImportant = 2;
306         rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
307         rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
308         rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
309     }
310     else
311     {
312         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
313         RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
314         bits = (char *)bmi + size +
315             DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
316         bch->bcBitCount = 1;
317         rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
318         rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
319     }
320
321     /* Create the AND bitmap */
322
323     hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
324                                bits, pInfo, DIB_RGB_COLORS );
325     ReleaseDC( 0, hdc );
326
327     /* Now create the CURSORICONINFO structure */
328
329     bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
330     bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
331     sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
332     sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
333
334     if (!(handle = GlobalAlloc( GMEM_MOVEABLE,
335                                 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
336     {
337         DeleteObject( hXorBits );
338         DeleteObject( hAndBits );
339         return 0;
340     }
341
342     /* Make it owned by the module */
343     if (hInstance) FarSetOwner( handle, GetExePtr(hInstance) );
344
345     info = (CURSORICONINFO *)GlobalLock( handle );
346     info->ptHotSpot.x   = hotspot.x;
347     info->ptHotSpot.y   = hotspot.y;
348     info->nWidth        = bmpXor->bitmap.bmWidth;
349     info->nHeight       = bmpXor->bitmap.bmHeight;
350     info->nWidthBytes   = bmpXor->bitmap.bmWidthBytes;
351     info->bPlanes       = bmpXor->bitmap.bmPlanes;
352     info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
353
354     /* Transfer the bitmap bits to the CURSORICONINFO structure */
355
356     GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
357     GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
358     DeleteObject( hXorBits );
359     DeleteObject( hAndBits );
360     GlobalUnlock( handle );
361     return handle;
362 }
363
364 /**********************************************************************
365  *          CURSORICON_Load
366  *
367  * Load a cursor or icon.
368  */
369 static HANDLE CURSORICON_Load( HANDLE hInstance, SEGPTR name, int width,
370                                int height, int colors, BOOL fCursor )
371 {
372     HANDLE handle,hRet;
373     HRSRC  hRsrc;
374     CURSORICONDIRENTRY dirEntry;
375
376     if (!hInstance)  /* OEM cursor/icon */
377     {
378         if (HIWORD(name))  /* Check for '#xxx' name */
379         {
380             char *ptr = PTR_SEG_TO_LIN( name );
381             if (ptr[0] != '#') return 0;
382             if (!(name = (SEGPTR)atoi( ptr + 1 ))) return 0;
383         }
384         return OBM_LoadCursorIcon( LOWORD(name), fCursor );
385     }
386
387     /* Find the best entry in the directory */
388
389     if (!CURSORICON_LoadDirEntry( hInstance, name, width, height,
390                                   colors, fCursor, &dirEntry )) return 0;
391
392     /* Load the resource */
393
394     if (!(hRsrc = FindResource( hInstance,
395                                 MAKEINTRESOURCE( dirEntry.icon.wResId ),
396                                 fCursor ? RT_CURSOR : RT_ICON ))) return 0;
397     if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
398
399     hRet = CURSORICON_LoadHandler( handle, hInstance, fCursor );
400     FreeResource(handle);
401     return hRet;
402 }
403
404
405 /***********************************************************************
406  *           CURSORICON_Copy
407  *
408  * Make a copy of a cursor or icon.
409  */
410 static HANDLE CURSORICON_Copy( HANDLE hInstance, HANDLE handle )
411 {
412     char *ptrOld, *ptrNew;
413     int size;
414     HANDLE hNew;
415
416     if (!(ptrOld = (char *)GlobalLock( handle ))) return 0;
417     if (!(hInstance = GetExePtr( hInstance ))) return 0;
418     size = GlobalSize( handle );
419     hNew = GlobalAlloc( GMEM_MOVEABLE, size );
420     FarSetOwner( hNew, hInstance );
421     ptrNew = (char *)GlobalLock( hNew );
422     memcpy( ptrNew, ptrOld, size );
423     GlobalUnlock( handle );
424     GlobalUnlock( hNew );
425     return hNew;
426 }
427
428 /***********************************************************************
429  *           CURSORICON_IconToCursor
430  *
431  * Should convert bitmap to mono and truncate if too large
432  * FIXME: if icon is passed returns a copy of OCR_DRAGOBJECT cursor
433  *        but should actually convert icon to cursor.
434  */
435 HCURSOR CURSORICON_IconToCursor(HICON hIcon)
436 {
437  CURSORICONINFO *ptr = NULL;
438
439  if(hIcon)
440     if (!(ptr = (CURSORICONINFO*)GlobalLock( hIcon ))) return FALSE;
441        if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
442           {
443             return hIcon; /* assuming it's a cursor */
444           }
445        else 
446           {
447            /* kludge */
448
449            HTASK hTask = GetCurrentTask();
450            TDB*  pTask = (TDB *)GlobalLock(hTask);
451
452            if(!pTask) return 0;
453
454            fprintf( stdnimp, "IconToCursor: Icons are not supported, returning default!\n");
455            return CURSORICON_Copy( pTask->hInstance ,
456                                    CURSORICON_Load(0,MAKEINTRESOURCE(OCR_DRAGOBJECT),
457                                                    SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE) );
458           }
459
460  return 0;
461 }
462
463 /***********************************************************************
464  *           LoadCursor    (USER.173)
465  */
466 HCURSOR LoadCursor( HANDLE hInstance, SEGPTR name )
467 {
468     if (HIWORD(name))
469         dprintf_cursor( stddeb, "LoadCursor: %04x '%s'\n",
470                         hInstance, (char *)PTR_SEG_TO_LIN( name ) );
471     else
472         dprintf_cursor( stddeb, "LoadCursor: %04x %04x\n",
473                         hInstance, LOWORD(name) );
474
475     return CURSORICON_Load( hInstance, name,
476                             SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
477 }
478
479
480 /***********************************************************************
481  *           LoadIcon    (USER.174)
482  */
483 HICON LoadIcon( HANDLE hInstance, SEGPTR name )
484 {
485     if (HIWORD(name))
486         dprintf_icon( stddeb, "LoadIcon: %04x '%s'\n",
487                       hInstance, (char *)PTR_SEG_TO_LIN( name ) );
488     else
489         dprintf_icon( stddeb, "LoadIcon: %04x %04x\n",
490                       hInstance, LOWORD(name) );
491
492     return CURSORICON_Load( hInstance, name,
493                             SYSMETRICS_CXICON, SYSMETRICS_CYICON,
494                             MIN( 16, 1 << screenDepth ), FALSE );
495 }
496
497
498 /***********************************************************************
499  *           CreateCursor    (USER.406)
500  */
501 HCURSOR CreateCursor( HINSTANCE hInstance, INT xHotSpot, INT yHotSpot,
502                       INT nWidth, INT nHeight,
503                       const BYTE *lpANDbits, const BYTE *lpXORbits )
504 {
505     CURSORICONINFO info = { { xHotSpot, yHotSpot }, nWidth, nHeight, 0, 1, 1 };
506
507     dprintf_cursor( stddeb, "CreateCursor: %dx%d spot=%d,%d xor=%p and=%p\n",
508                     nWidth, nHeight, xHotSpot, yHotSpot, lpXORbits, lpANDbits);
509     return CreateCursorIconIndirect( hInstance, &info, lpANDbits, lpXORbits );
510 }
511
512
513 /***********************************************************************
514  *           CreateIcon    (USER.407)
515  */
516 HICON CreateIcon( HINSTANCE hInstance, INT nWidth, INT nHeight, BYTE bPlanes,
517                   BYTE bBitsPixel, const BYTE* lpANDbits, const BYTE* lpXORbits)
518 {
519     CURSORICONINFO info = { { 0, 0 }, nWidth, nHeight, 0, bPlanes, bBitsPixel };
520
521     dprintf_icon( stddeb, "CreateIcon: %dx%dx%d, xor=%p, and=%p\n",
522                   nWidth, nHeight, bPlanes * bBitsPixel, lpXORbits, lpANDbits);
523     return CreateCursorIconIndirect( hInstance, &info, lpANDbits, lpXORbits );
524 }
525
526
527 /***********************************************************************
528  *           CreateCursorIconIndirect    (USER.408)
529  */
530 HANDLE CreateCursorIconIndirect( HANDLE hInstance, CURSORICONINFO *info,
531                                  const BYTE *lpANDbits, const BYTE *lpXORbits )
532 {
533     HANDLE handle;
534     char *ptr;
535     int sizeAnd, sizeXor;
536
537     hInstance = GetExePtr( hInstance );  /* Make it a module handle */
538     if (!hInstance || !lpXORbits || !lpANDbits || info->bPlanes != 1) return 0;
539     info->nWidthBytes = (info->nWidth * info->bBitsPerPixel + 15) / 16 * 2;
540     sizeXor = info->nHeight * info->nWidthBytes;
541     sizeAnd = info->nHeight * ((info->nWidth + 15) / 16 * 2);
542     if (!(handle = DirectResAlloc(hInstance, 0x10,
543                                   sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
544         return 0;
545     ptr = (char *)GlobalLock( handle );
546     memcpy( ptr, info, sizeof(*info) );
547     memcpy( ptr + sizeof(CURSORICONINFO), lpANDbits, sizeAnd );
548     memcpy( ptr + sizeof(CURSORICONINFO) + sizeAnd, lpXORbits, sizeXor );
549     GlobalUnlock( handle );
550     return handle;
551 }
552
553
554 /***********************************************************************
555  *           CopyIcon    (USER.368)
556  */
557 #ifdef WINELIB
558 HICON CopyIcon( HICON hIcon )
559 {
560     dprintf_icon( stddeb, "CopyIcon: %04x\n", hIcon );
561     return CURSORICON_Copy( 0, hIcon );
562 }
563 #else
564 HICON CopyIcon( HANDLE hInstance, HICON hIcon )
565 {
566     dprintf_icon( stddeb, "CopyIcon: %04x %04x\n", hInstance, hIcon );
567     return CURSORICON_Copy( hInstance, hIcon );
568 }
569 #endif
570
571
572 /***********************************************************************
573  *           CopyCursor    (USER.369)
574  */
575 #ifdef WINELIB
576 HCURSOR CopyCursor( HCURSOR hCursor )
577 {
578     dprintf_cursor( stddeb, "CopyCursor: %04x\n", hCursor );
579     return CURSORICON_Copy( 0, hCursor );
580 }
581 #else
582 HCURSOR CopyCursor( HANDLE hInstance, HCURSOR hCursor )
583 {
584     dprintf_cursor( stddeb, "CopyCursor: %04x %04x\n", hInstance, hCursor );
585     return CURSORICON_Copy( hInstance, hCursor );
586 }
587 #endif
588
589
590 /***********************************************************************
591  *           DestroyIcon    (USER.457)
592  */
593 BOOL DestroyIcon( HICON hIcon )
594 {
595     dprintf_icon( stddeb, "DestroyIcon: %04x\n", hIcon );
596     /* FIXME: should check for OEM icon here */
597     return (GlobalFree( hIcon ) != 0);
598 }
599
600
601 /***********************************************************************
602  *           DestroyCursor    (USER.458)
603  */
604 BOOL DestroyCursor( HCURSOR hCursor )
605 {
606     dprintf_cursor( stddeb, "DestroyCursor: %04x\n", hCursor );
607     /* FIXME: should check for OEM cursor here */
608     return (GlobalFree( hCursor ) != 0);
609 }
610
611
612 /***********************************************************************
613  *           DrawIcon    (USER.84)
614  */
615 BOOL DrawIcon( HDC hdc, INT x, INT y, HICON hIcon )
616 {
617     CURSORICONINFO *ptr;
618     HDC hMemDC;
619     HBITMAP hXorBits, hAndBits;
620     COLORREF oldFg, oldBg;
621
622     if (!(ptr = (CURSORICONINFO *)GlobalLock( hIcon ))) return FALSE;
623     if (!(hMemDC = CreateCompatibleDC( hdc ))) return FALSE;
624     hAndBits = CreateBitmap( ptr->nWidth, ptr->nHeight, 1, 1, (char *)(ptr+1));
625     hXorBits = CreateBitmap( ptr->nWidth, ptr->nHeight, ptr->bPlanes,
626                              ptr->bBitsPerPixel, (char *)(ptr + 1)
627                               + ptr->nHeight * ((ptr->nWidth + 15) / 16 * 2) );
628     oldFg = SetTextColor( hdc, RGB(0,0,0) );
629     oldBg = SetBkColor( hdc, RGB(255,255,255) );
630
631     if (hXorBits && hAndBits)
632     {
633         HBITMAP hBitTemp = SelectObject( hMemDC, hAndBits );
634         BitBlt( hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0, SRCAND );
635         SelectObject( hMemDC, hXorBits );
636         BitBlt( hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0, SRCINVERT);
637         SelectObject( hMemDC, hBitTemp );
638     }
639     DeleteDC( hMemDC );
640     if (hXorBits) DeleteObject( hXorBits );
641     if (hAndBits) DeleteObject( hAndBits );
642     GlobalUnlock( hIcon );
643     SetTextColor( hdc, oldFg );
644     SetBkColor( hdc, oldBg );
645     return TRUE;
646 }
647
648
649 /***********************************************************************
650  *           DumpIcon    (USER.459)
651  */
652 DWORD DumpIcon( SEGPTR pInfo, WORD *lpLen,
653                 SEGPTR *lpXorBits, SEGPTR *lpAndBits )
654 {
655     CURSORICONINFO *info = PTR_SEG_TO_LIN( pInfo );
656     int sizeAnd, sizeXor;
657
658     if (!info) return 0;
659     sizeXor = info->nHeight * info->nWidthBytes;
660     sizeAnd = info->nHeight * ((info->nWidth + 15) / 16 * 2);
661     if (lpAndBits) *lpAndBits = pInfo + sizeof(CURSORICONINFO);
662     if (lpXorBits) *lpXorBits = pInfo + sizeof(CURSORICONINFO) + sizeAnd;
663     if (lpLen) *lpLen = sizeof(CURSORICONINFO) + sizeAnd + sizeXor;
664     return MAKELONG( sizeXor, sizeXor );
665 }
666
667
668 /***********************************************************************
669  *           CURSORICON_SetCursor
670  *
671  * Change the X cursor. Helper function for SetCursor() and ShowCursor().
672  */
673 static BOOL CURSORICON_SetCursor( HCURSOR hCursor )
674 {
675     Pixmap pixmapBits, pixmapMask, pixmapAll;
676     XColor fg, bg;
677     Cursor cursor = None;
678
679     if (!hCursor)  /* Create an empty cursor */
680     {
681         static const char data[] = { 0 };
682
683         bg.red = bg.green = bg.blue = 0x0000;
684         pixmapBits = XCreateBitmapFromData( display, rootWindow, data, 1, 1 );
685         if (pixmapBits)
686         {
687             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
688                                           &bg, &bg, 0, 0 );
689             XFreePixmap( display, pixmapBits );
690         }
691     }
692     else  /* Create the X cursor from the bits */
693     {
694         CURSORICONINFO *ptr;
695         XImage *image;
696
697         if (!(ptr = (CURSORICONINFO*)GlobalLock( hCursor ))) return FALSE;
698         if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
699         {
700             fprintf( stderr, "Cursor %04x has more than 1 bpp!\n", hCursor );
701             return FALSE;
702         }
703
704         /* Create a pixmap and transfer all the bits to it */
705
706         pixmapAll = XCreatePixmap( display, rootWindow,
707                                    ptr->nWidth, ptr->nHeight * 2, 1 );
708         image = XCreateImage( display, DefaultVisualOfScreen(screen),
709                               1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
710                               ptr->nHeight * 2, 16, ptr->nWidthBytes);
711         if (image)
712         {
713             extern void _XInitImageFuncPtrs( XImage* );
714             image->byte_order = MSBFirst;
715             image->bitmap_bit_order = MSBFirst;
716             image->bitmap_unit = 16;
717             _XInitImageFuncPtrs(image);
718             if (pixmapAll)
719                 CallTo32_LargeStack( XPutImage, 10,
720                                      display, pixmapAll, BITMAP_monoGC, image,
721                                      0, 0, 0, 0, ptr->nWidth, ptr->nHeight*2 );
722             image->data = NULL;
723             XDestroyImage( image );
724         }
725
726         /* Now create the 2 pixmaps for bits and mask */
727
728         pixmapBits = XCreatePixmap( display, rootWindow,
729                                     ptr->nWidth, ptr->nHeight, 1 );
730         pixmapMask = XCreatePixmap( display, rootWindow,
731                                     ptr->nWidth, ptr->nHeight, 1 );
732
733         /* Make sure everything went OK so far */
734
735         if (pixmapBits && pixmapMask && pixmapAll)
736         {
737             /* We have to do some magic here, as cursors are not fully
738              * compatible between Windows and X11. Under X11, there
739              * are only 3 possible color cursor: black, white and
740              * masked. So we map the 4th Windows color (invert the
741              * bits on the screen) to black. This require some boolean
742              * arithmetic:
743              *
744              *         Windows          |          X11
745              * Xor    And      Result   |   Bits     Mask     Result
746              *  0      0     black      |    0        1     background
747              *  0      1     no change  |    X        0     no change
748              *  1      0     white      |    1        1     foreground
749              *  1      1     inverted   |    0        1     background
750              *
751              * which gives:
752              *  Bits = 'Xor' and not 'And'
753              *  Mask = 'Xor' or not 'And'
754              *
755              * FIXME: apparently some servers do support 'inverted' color.
756              * I don't know if it's correct per the X spec, but maybe
757              * we ought to take advantage of it.  -- AJ
758              */
759             XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
760                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
761             XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
762                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
763             XSetFunction( display, BITMAP_monoGC, GXandReverse );
764             XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
765                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
766             XSetFunction( display, BITMAP_monoGC, GXorReverse );
767             XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
768                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
769             XSetFunction( display, BITMAP_monoGC, GXcopy );
770             fg.red = fg.green = fg.blue = 0xffff;
771             bg.red = bg.green = bg.blue = 0x0000;
772             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
773                                 &fg, &bg, ptr->ptHotSpot.x, ptr->ptHotSpot.y );
774         }
775
776         /* Now free everything */
777
778         if (pixmapAll) XFreePixmap( display, pixmapAll );
779         if (pixmapBits) XFreePixmap( display, pixmapBits );
780         if (pixmapMask) XFreePixmap( display, pixmapMask );
781         GlobalUnlock( hCursor );
782     }
783
784     if (cursor == None) return FALSE;
785     if (CURSORICON_XCursor != None) XFreeCursor( display, CURSORICON_XCursor );
786     CURSORICON_XCursor = cursor;
787
788     if (rootWindow != DefaultRootWindow(display))
789     {
790         /* Set the cursor on the desktop window */
791         XDefineCursor( display, rootWindow, cursor );
792     }
793     else
794     {
795         /* Set the same cursor for all top-level windows */
796         HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
797         while(hwnd)
798         {
799             Window win = WIN_GetXWindow( hwnd );
800             if (win) XDefineCursor( display, win, cursor );
801             hwnd = GetWindow( hwnd, GW_HWNDNEXT );
802         }
803     }
804     return TRUE;
805 }
806
807
808 /***********************************************************************
809  *           SetCursor    (USER.69)
810  */
811 HCURSOR SetCursor( HCURSOR hCursor )
812 {
813     HCURSOR hOldCursor;
814
815     if (hCursor == hActiveCursor) return hActiveCursor;  /* No change */
816     dprintf_cursor( stddeb, "SetCursor: %04x\n", hCursor );
817     hOldCursor = hActiveCursor;
818     hActiveCursor = hCursor;
819     /* Change the cursor shape only if it is visible */
820     if (CURSOR_ShowCount >= 0) CURSORICON_SetCursor( hActiveCursor );
821     return hOldCursor;
822 }
823
824
825 /***********************************************************************
826  *           SetCursorPos    (USER.70)
827  */
828 void SetCursorPos( short x, short y )
829 {
830     dprintf_cursor( stddeb, "SetCursorPos: x=%d y=%d\n", x, y );
831     XWarpPointer( display, rootWindow, rootWindow, 0, 0, 0, 0, x, y );
832 }
833
834
835 /***********************************************************************
836  *           ShowCursor    (USER.71)
837  */
838 int ShowCursor( BOOL bShow )
839 {
840     dprintf_cursor( stddeb, "ShowCursor: %d, count=%d\n",
841                     bShow, CURSOR_ShowCount );
842
843     if (bShow)
844     {
845         if (++CURSOR_ShowCount == 0)
846             CURSORICON_SetCursor( hActiveCursor );  /* Show it */
847     }
848     else
849     {
850         if (--CURSOR_ShowCount == -1)
851             CURSORICON_SetCursor( 0 );  /* Hide it */
852     }
853     return CURSOR_ShowCount;
854 }
855
856
857 /***********************************************************************
858  *           GetCursor    (USER.247)
859  */
860 HCURSOR GetCursor(void)
861 {
862     return hActiveCursor;
863 }
864
865
866 /***********************************************************************
867  *           ClipCursor    (USER.16)
868  */
869 BOOL ClipCursor( RECT *rect )
870 {
871     if (!rect) SetRectEmpty( &CURSOR_ClipRect );
872     else CopyRect( &CURSOR_ClipRect, rect );
873     return TRUE;
874 }
875
876
877 /***********************************************************************
878  *           GetCursorPos    (USER.17)
879  */
880 void GetCursorPos( POINT *pt )
881 {
882     Window root, child;
883     int rootX, rootY, childX, childY;
884     unsigned int mousebut;
885
886     if (!pt) return;
887     if (!XQueryPointer( display, rootWindow, &root, &child,
888                         &rootX, &rootY, &childX, &childY, &mousebut ))
889         pt->x = pt->y = 0;
890     else
891     {
892         pt->x = rootX + desktopX;
893         pt->y = rootY + desktopY;
894     }
895     dprintf_cursor(stddeb, "GetCursorPos: ret=%d,%d\n", pt->x, pt->y );
896 }
897
898
899 /***********************************************************************
900  *           GetClipCursor    (USER.309)
901  */
902 void GetClipCursor( RECT *rect )
903 {
904     if (rect) CopyRect( rect, &CURSOR_ClipRect );
905 }
906
907
908 /**********************************************************************
909  *          GetIconID    (USER.455)
910  */
911 WORD GetIconID( HANDLE hResource, DWORD resType )
912 {
913     CURSORICONDIR *lpDir = LockResource(hResource);
914
915     if (!lpDir || lpDir->idReserved ||
916         ((lpDir->idType != 1) && (lpDir->idType != 2)))
917     {
918         dprintf_cursor(stddeb,"GetIconID: invalid resource directory\n");
919         return 0;
920     }
921
922     dprintf_cursor( stddeb, "GetIconID: hRes=%04x, entries=%i\n",
923                     hResource, lpDir->idCount );
924
925     switch(resType)
926     {
927     case 1:  /* cursor */
928         {
929             CURSORDIRENTRY *entry = CURSORICON_FindBestCursor( lpDir,
930                                     SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR );
931             return entry ? entry->wResId : 0;
932         }
933     case 3:  /* icon */
934         {
935             ICONDIRENTRY *entry = CURSORICON_FindBestIcon( lpDir,
936                                           SYSMETRICS_CXICON, SYSMETRICS_CYICON,
937                                           MIN( 16, 1 << screenDepth ) );
938             return entry ? entry->wResId : 0;
939         }
940     }
941     fprintf( stderr, "GetIconID: invalid res type %ld\n", resType );
942     return 0;
943 }
944
945
946 /**********************************************************************
947  *          LoadIconHandler    (USER.456)
948  */
949 HICON LoadIconHandler( HANDLE hResource, BOOL bNew )
950 {
951     dprintf_cursor(stddeb,"LoadIconHandler: hRes=%04x\n",hResource);
952
953     if( !bNew )
954       {
955         fprintf(stdnimp,"LoadIconHandler: 2.xx resources are not supported\n");
956         return 0;
957       }
958     return CURSORICON_LoadHandler( hResource, 0, FALSE);
959 }