Release 951226
[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_Load
231  *
232  * Load a cursor or icon.
233  */
234 static HANDLE CURSORICON_Load( HANDLE hInstance, SEGPTR name, int width,
235                                int height, int colors, BOOL fCursor )
236 {
237     HANDLE handle, hAndBits, hXorBits;
238     HRSRC hRsrc;
239     HDC hdc;
240     int size, sizeAnd, sizeXor;
241     POINT hotspot = { 0 ,0 };
242     BITMAPOBJ *bmpXor, *bmpAnd;
243     BITMAPINFO *bmi, *pInfo;
244     CURSORICONINFO *info;
245     CURSORICONDIRENTRY dirEntry;
246     char *bits;
247
248     if (!hInstance)  /* OEM cursor/icon */
249     {
250         if (HIWORD(name))  /* Check for '#xxx' name */
251         {
252             char *ptr = PTR_SEG_TO_LIN( name );
253             if (ptr[0] != '#') return 0;
254             if (!(name = (SEGPTR)atoi( ptr + 1 ))) return 0;
255         }
256         return OBM_LoadCursorIcon( LOWORD(name), fCursor );
257     }
258
259     /* Find the best entry in the directory */
260
261     if (!CURSORICON_LoadDirEntry( hInstance, name, width, height,
262                                   colors, fCursor, &dirEntry )) return 0;
263
264     /* Load the resource */
265
266     if (!(hRsrc = FindResource( hInstance,
267                                 MAKEINTRESOURCE( dirEntry.icon.wResId ),
268                                 fCursor ? RT_CURSOR : RT_ICON ))) return 0;
269     if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
270
271     if (fCursor)  /* If cursor, get the hotspot */
272     {
273         POINT *pt = (POINT *)LockResource( handle );
274         hotspot = *pt;
275         bmi = (BITMAPINFO *)(pt + 1);
276     }
277     else bmi = (BITMAPINFO *)LockResource( handle );
278
279     /* Create a copy of the bitmap header */
280
281     size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
282     /* Make sure we have room for the monochrome bitmap later on */
283     size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
284     pInfo = (BITMAPINFO *)xmalloc( size );
285     memcpy( pInfo, bmi, size );
286
287     if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
288     {
289         if (pInfo->bmiHeader.biCompression != BI_RGB)
290         {
291             fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
292             FreeResource( handle );
293             free( pInfo );
294             return 0;
295         }
296         pInfo->bmiHeader.biHeight /= 2;
297     }
298     else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
299     {
300         BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
301         core->bcHeight /= 2;
302     }
303     else
304     {
305         fprintf( stderr, "CURSORICON_Load: Unknown bitmap length %ld!\n",
306                  pInfo->bmiHeader.biSize );
307         FreeResource( handle );
308         free( pInfo );
309         return 0;
310     }
311
312     /* Create the XOR bitmap */
313
314     if (!(hdc = GetDC( 0 )))
315     {
316         FreeResource( handle );
317         free( pInfo );
318         return 0;
319     }
320
321     hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
322                                (char*)bmi + size, pInfo, DIB_RGB_COLORS );
323
324     /* Fix the bitmap header to load the monochrome mask */
325
326     if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
327     {
328         BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
329         RGBQUAD *rgb = pInfo->bmiColors;
330         bits = (char *)bmi + size +
331             DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
332         bih->biBitCount = 1;
333         bih->biClrUsed = bih->biClrImportant = 2;
334         rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
335         rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
336         rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
337     }
338     else
339     {
340         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
341         RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
342         bits = (char *)bmi + size +
343             DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
344         bch->bcBitCount = 1;
345         rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
346         rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
347     }
348
349     /* Create the AND bitmap */
350
351     hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
352                                bits, pInfo, DIB_RGB_COLORS );
353     ReleaseDC( 0, hdc );
354     FreeResource( handle );
355
356     /* Now create the CURSORICONINFO structure */
357
358     bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
359     bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
360     sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
361     sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
362
363     if (!(handle = GlobalAlloc( GMEM_MOVEABLE,
364                                 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
365     {
366         DeleteObject( hXorBits );
367         DeleteObject( hAndBits );
368         return 0;
369     }
370     /* Make it owned by the module */
371     FarSetOwner( handle, (WORD)(DWORD)GetExePtr( hInstance ) );
372     info = (CURSORICONINFO *)GlobalLock( handle );
373     info->ptHotSpot.x   = hotspot.x;
374     info->ptHotSpot.y   = hotspot.y;
375     info->nWidth        = bmpXor->bitmap.bmWidth;
376     info->nHeight       = bmpXor->bitmap.bmHeight;
377     info->nWidthBytes   = bmpXor->bitmap.bmWidthBytes;
378     info->bPlanes       = bmpXor->bitmap.bmPlanes;
379     info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
380
381     /* Transfer the bitmap bits to the CURSORICONINFO structure */
382
383     GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
384     GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
385     DeleteObject( hXorBits );
386     DeleteObject( hAndBits );
387     GlobalUnlock( handle );
388     return handle;
389 }
390
391
392 /***********************************************************************
393  *           CURSORICON_Copy
394  *
395  * Make a copy of a cursor or icon.
396  */
397 static HANDLE CURSORICON_Copy( HANDLE hInstance, HANDLE handle )
398 {
399     char *ptrOld, *ptrNew;
400     int size;
401     HANDLE hNew;
402
403     if (!(ptrOld = (char *)GlobalLock( handle ))) return 0;
404     if (!(hInstance = GetExePtr( hInstance ))) return 0;
405     size = GlobalSize( handle );
406     hNew = GlobalAlloc( GMEM_MOVEABLE, size );
407     FarSetOwner( hNew, (WORD)(DWORD)hInstance );
408     ptrNew = (char *)GlobalLock( hNew );
409     memcpy( ptrNew, ptrOld, size );
410     GlobalUnlock( handle );
411     GlobalUnlock( hNew );
412     return hNew;
413 }
414
415 /***********************************************************************
416  *           CURSORICON_IconToCursor
417  *
418  * Should convert bitmap to mono and truncate if too large
419  * FIXME: if icon is passed returns a copy of OCR_DRAGOBJECT cursor
420  *        but should actually convert icon to cursor.
421  */
422 HCURSOR CURSORICON_IconToCursor(HICON hIcon)
423 {
424  CURSORICONINFO *ptr = NULL;
425
426  if(hIcon)
427     if (!(ptr = (CURSORICONINFO*)GlobalLock( hIcon ))) return FALSE;
428        if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
429           {
430             return hIcon; /* assuming it's a cursor */
431           }
432        else 
433           {
434            /* kludge */
435
436            HTASK hTask = GetCurrentTask();
437            TDB*  pTask = (TDB *)GlobalLock(hTask);
438
439            if(!pTask) return 0;
440
441            fprintf( stdnimp, "IconToCursor: Icons are not supported, returning default!\n");
442            return CURSORICON_Copy( pTask->hInstance ,
443                                    CURSORICON_Load(0,MAKEINTRESOURCE(OCR_DRAGOBJECT),
444                                                    SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE) );
445           }
446
447  return 0;
448 }
449
450 /***********************************************************************
451  *           LoadCursor    (USER.173)
452  */
453 HCURSOR LoadCursor( HANDLE hInstance, SEGPTR name )
454 {
455     if (HIWORD(name))
456         dprintf_cursor( stddeb, "LoadCursor: "NPFMT" '%s'\n",
457                         hInstance, (char *)PTR_SEG_TO_LIN( name ) );
458     else
459         dprintf_cursor( stddeb, "LoadCursor: "NPFMT" %04x\n",
460                         hInstance, LOWORD(name) );
461
462     return CURSORICON_Load( hInstance, name,
463                             SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
464 }
465
466
467 /***********************************************************************
468  *           LoadIcon    (USER.174)
469  */
470 HICON LoadIcon( HANDLE hInstance, SEGPTR name )
471 {
472     if (HIWORD(name))
473         dprintf_icon( stddeb, "LoadIcon: "NPFMT" '%s'\n",
474                       hInstance, (char *)PTR_SEG_TO_LIN( name ) );
475     else
476         dprintf_icon( stddeb, "LoadIcon: "NPFMT" %04x\n",
477                       hInstance, LOWORD(name) );
478
479     return CURSORICON_Load( hInstance, name,
480                             SYSMETRICS_CXICON, SYSMETRICS_CYICON,
481                             MIN( 16, 1 << screenDepth ), FALSE );
482 }
483
484
485 /***********************************************************************
486  *           CreateCursor    (USER.406)
487  */
488 HICON CreateCursor( HANDLE hInstance, INT xHotSpot, INT yHotSpot,
489                     INT nWidth, INT nHeight, LPSTR lpANDbits, LPSTR lpXORbits)
490 {
491     CURSORICONINFO info = { { xHotSpot, yHotSpot }, nWidth, nHeight, 0, 1, 1 };
492
493     dprintf_cursor( stddeb, "CreateCursor: %dx%d spot=%d,%d xor=%p and=%p\n",
494                     nWidth, nHeight, xHotSpot, yHotSpot, lpXORbits, lpANDbits);
495     return CreateCursorIconIndirect( hInstance, &info, lpANDbits, lpXORbits );
496 }
497
498
499 /***********************************************************************
500  *           CreateIcon    (USER.407)
501  */
502 HICON CreateIcon( HANDLE hInstance, INT nWidth, INT nHeight, BYTE bPlanes,
503                   BYTE bBitsPixel, LPSTR lpANDbits, LPSTR lpXORbits)
504 {
505     CURSORICONINFO info = { { 0, 0 }, nWidth, nHeight, 0, bPlanes, bBitsPixel };
506
507     dprintf_icon( stddeb, "CreateIcon: %dx%dx%d, xor=%p, and=%p\n",
508                   nWidth, nHeight, bPlanes * bBitsPixel, lpXORbits, lpANDbits);
509     return CreateCursorIconIndirect( hInstance, &info, lpANDbits, lpXORbits );
510 }
511
512
513 /***********************************************************************
514  *           CreateCursorIconIndirect    (USER.408)
515  */
516 HANDLE CreateCursorIconIndirect( HANDLE hInstance, CURSORICONINFO *info,
517                                  LPSTR lpANDbits, LPSTR lpXORbits )
518 {
519     HANDLE handle;
520     char *ptr;
521     int sizeAnd, sizeXor;
522
523     hInstance = GetExePtr( hInstance );  /* Make it a module handle */
524     if (!hInstance || !lpXORbits || !lpANDbits || info->bPlanes != 1) return 0;
525     info->nWidthBytes = (info->nWidth * info->bBitsPerPixel + 15) / 16 * 2;
526     sizeXor = info->nHeight * info->nWidthBytes;
527     sizeAnd = info->nHeight * ((info->nWidth + 15) / 16 * 2);
528     if (!(handle = DirectResAlloc(hInstance, 0x10,
529                                   sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
530         return 0;
531     ptr = (char *)GlobalLock( handle );
532     memcpy( ptr, info, sizeof(*info) );
533     memcpy( ptr + sizeof(CURSORICONINFO), lpANDbits, sizeAnd );
534     memcpy( ptr + sizeof(CURSORICONINFO) + sizeAnd, lpXORbits, sizeXor );
535     GlobalUnlock( handle );
536     return handle;
537 }
538
539
540 /***********************************************************************
541  *           CopyIcon    (USER.368)
542  */
543 HICON CopyIcon( HANDLE hInstance, HICON hIcon )
544 {
545     dprintf_icon( stddeb, "CopyIcon: "NPFMT" "NPFMT"\n", hInstance, hIcon );
546     return CURSORICON_Copy( hInstance, hIcon );
547 }
548
549
550 /***********************************************************************
551  *           CopyCursor    (USER.369)
552  */
553 HCURSOR CopyCursor( HANDLE hInstance, HCURSOR hCursor )
554 {
555     dprintf_cursor( stddeb, "CopyCursor: "NPFMT" "NPFMT"\n", hInstance, hCursor );
556     return CURSORICON_Copy( hInstance, hCursor );
557 }
558
559
560 /***********************************************************************
561  *           DestroyIcon    (USER.457)
562  */
563 BOOL DestroyIcon( HICON hIcon )
564 {
565     dprintf_icon( stddeb, "DestroyIcon: "NPFMT"\n", hIcon );
566     /* FIXME: should check for OEM icon here */
567     return (GlobalFree( hIcon ) != 0);
568 }
569
570
571 /***********************************************************************
572  *           DestroyCursor    (USER.458)
573  */
574 BOOL DestroyCursor( HCURSOR hCursor )
575 {
576     dprintf_cursor( stddeb, "DestroyCursor: "NPFMT"\n", hCursor );
577     /* FIXME: should check for OEM cursor here */
578     return (GlobalFree( hCursor ) != 0);
579 }
580
581
582 /***********************************************************************
583  *           DrawIcon    (USER.84)
584  */
585 BOOL DrawIcon( HDC hdc, short x, short y, HICON hIcon )
586 {
587     CURSORICONINFO *ptr;
588     HDC hMemDC;
589     HBITMAP hXorBits, hAndBits;
590     COLORREF oldFg, oldBg;
591
592     if (!(ptr = (CURSORICONINFO *)GlobalLock( hIcon ))) return FALSE;
593     if (!(hMemDC = CreateCompatibleDC( hdc ))) return FALSE;
594     hAndBits = CreateBitmap( ptr->nWidth, ptr->nHeight, 1, 1, (char *)(ptr+1));
595     hXorBits = CreateBitmap( ptr->nWidth, ptr->nHeight, ptr->bPlanes,
596                              ptr->bBitsPerPixel, (char *)(ptr + 1)
597                               + ptr->nHeight * ((ptr->nWidth + 15) / 16 * 2) );
598     oldFg = SetTextColor( hdc, RGB(0,0,0) );
599     oldBg = SetBkColor( hdc, RGB(255,255,255) );
600
601     if (hXorBits && hAndBits)
602     {
603         HBITMAP hBitTemp = SelectObject( hMemDC, hAndBits );
604         BitBlt( hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0, SRCAND );
605         SelectObject( hMemDC, hXorBits );
606         BitBlt( hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0, SRCINVERT);
607         SelectObject( hMemDC, hBitTemp );
608     }
609     DeleteDC( hMemDC );
610     if (hXorBits) DeleteObject( hXorBits );
611     if (hAndBits) DeleteObject( hAndBits );
612     GlobalUnlock( hIcon );
613     SetTextColor( hdc, oldFg );
614     SetBkColor( hdc, oldBg );
615     return TRUE;
616 }
617
618
619 /***********************************************************************
620  *           DumpIcon    (USER.459)
621  */
622 DWORD DumpIcon( SEGPTR pInfo, WORD *lpLen,
623                 SEGPTR *lpXorBits, SEGPTR *lpAndBits )
624 {
625     CURSORICONINFO *info = PTR_SEG_TO_LIN( pInfo );
626     int sizeAnd, sizeXor;
627
628     if (!info) return 0;
629     sizeXor = info->nHeight * info->nWidthBytes;
630     sizeAnd = info->nHeight * ((info->nWidth + 15) / 16 * 2);
631     if (lpAndBits) *lpAndBits = pInfo + sizeof(CURSORICONINFO);
632     if (lpXorBits) *lpXorBits = pInfo + sizeof(CURSORICONINFO) + sizeAnd;
633     if (lpLen) *lpLen = sizeof(CURSORICONINFO) + sizeAnd + sizeXor;
634     return MAKELONG( sizeXor, sizeXor );
635 }
636
637
638 /***********************************************************************
639  *           CURSORICON_SetCursor
640  *
641  * Change the X cursor. Helper function for SetCursor() and ShowCursor().
642  */
643 static BOOL CURSORICON_SetCursor( HCURSOR hCursor )
644 {
645     Pixmap pixmapBits, pixmapMask, pixmapAll;
646     XColor fg, bg;
647     Cursor cursor = None;
648
649     if (!hCursor)  /* Create an empty cursor */
650     {
651         static const char data[] = { 0 };
652
653         bg.red = bg.green = bg.blue = 0x0000;
654         pixmapBits = XCreateBitmapFromData( display, rootWindow, data, 1, 1 );
655         if (pixmapBits)
656         {
657             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
658                                           &bg, &bg, 0, 0 );
659             XFreePixmap( display, pixmapBits );
660         }
661     }
662     else  /* Create the X cursor from the bits */
663     {
664         CURSORICONINFO *ptr;
665         XImage *image;
666
667         if (!(ptr = (CURSORICONINFO*)GlobalLock( hCursor ))) return FALSE;
668         if (ptr->bPlanes * ptr->bBitsPerPixel != 1)
669         {
670             fprintf( stderr, "Cursor "NPFMT" has more than 1 bpp!\n", hCursor );
671             return FALSE;
672         }
673
674         /* Create a pixmap and transfer all the bits to it */
675
676         pixmapAll = XCreatePixmap( display, rootWindow,
677                                    ptr->nWidth, ptr->nHeight * 2, 1 );
678         image = XCreateImage( display, DefaultVisualOfScreen(screen),
679                               1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
680                               ptr->nHeight * 2, 16, ptr->nWidthBytes);
681         if (image)
682         {
683             extern void _XInitImageFuncPtrs( XImage* );
684             image->byte_order = MSBFirst;
685             image->bitmap_bit_order = MSBFirst;
686             image->bitmap_unit = 16;
687             _XInitImageFuncPtrs(image);
688             if (pixmapAll)
689                 CallTo32_LargeStack( XPutImage, 10,
690                                      display, pixmapAll, BITMAP_monoGC, image,
691                                      0, 0, 0, 0, ptr->nWidth, ptr->nHeight*2 );
692             image->data = NULL;
693             XDestroyImage( image );
694         }
695
696         /* Now create the 2 pixmaps for bits and mask */
697
698         pixmapBits = XCreatePixmap( display, rootWindow,
699                                     ptr->nWidth, ptr->nHeight, 1 );
700         pixmapMask = XCreatePixmap( display, rootWindow,
701                                     ptr->nWidth, ptr->nHeight, 1 );
702
703         /* Make sure everything went OK so far */
704
705         if (pixmapBits && pixmapMask && pixmapAll)
706         {
707             /* We have to do some magic here, as cursors are not fully
708              * compatible between Windows and X11. Under X11, there
709              * are only 3 possible color cursor: black, white and
710              * masked. So we map the 4th Windows color (invert the
711              * bits on the screen) to black. This require some boolean
712              * arithmetic:
713              *
714              *         Windows          |          X11
715              * Xor    And      Result   |   Bits     Mask     Result
716              *  0      0     black      |    0        1     background
717              *  0      1     no change  |    X        0     no change
718              *  1      0     white      |    1        1     foreground
719              *  1      1     inverted   |    0        1     background
720              *
721              * which gives:
722              *  Bits = 'Xor' and not 'And'
723              *  Mask = 'Xor' or not 'And'
724              *
725              * FIXME: apparently some servers do support 'inverted' color.
726              * I don't know if it's correct per the X spec, but maybe
727              * we ought to take advantage of it.  -- AJ
728              */
729             XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
730                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
731             XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
732                        0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
733             XSetFunction( display, BITMAP_monoGC, GXandReverse );
734             XCopyArea( display, pixmapAll, pixmapBits, BITMAP_monoGC,
735                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
736             XSetFunction( display, BITMAP_monoGC, GXorReverse );
737             XCopyArea( display, pixmapAll, pixmapMask, BITMAP_monoGC,
738                        0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
739             XSetFunction( display, BITMAP_monoGC, GXcopy );
740             fg.red = fg.green = fg.blue = 0xffff;
741             bg.red = bg.green = bg.blue = 0x0000;
742             cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
743                                 &fg, &bg, ptr->ptHotSpot.x, ptr->ptHotSpot.y );
744         }
745
746         /* Now free everything */
747
748         if (pixmapAll) XFreePixmap( display, pixmapAll );
749         if (pixmapBits) XFreePixmap( display, pixmapBits );
750         if (pixmapMask) XFreePixmap( display, pixmapMask );
751         GlobalUnlock( hCursor );
752     }
753
754     if (cursor == None) return FALSE;
755     if (CURSORICON_XCursor != None) XFreeCursor( display, CURSORICON_XCursor );
756     CURSORICON_XCursor = cursor;
757
758     if (rootWindow != DefaultRootWindow(display))
759     {
760         /* Set the cursor on the desktop window */
761         XDefineCursor( display, rootWindow, cursor );
762     }
763     else
764     {
765         /* Set the same cursor for all top-level windows */
766         HWND hwnd = GetWindow( GetDesktopWindow(), GW_CHILD );
767         while(hwnd)
768         {
769             Window win = WIN_GetXWindow( hwnd );
770             if (win) XDefineCursor( display, win, cursor );
771             hwnd = GetWindow( hwnd, GW_HWNDNEXT );
772         }
773     }
774     return TRUE;
775 }
776
777
778 /***********************************************************************
779  *           SetCursor    (USER.69)
780  */
781 HCURSOR SetCursor( HCURSOR hCursor )
782 {
783     HCURSOR hOldCursor;
784
785     if (hCursor == hActiveCursor) return hActiveCursor;  /* No change */
786     dprintf_cursor( stddeb, "SetCursor: "NPFMT"\n", hCursor );
787     hOldCursor = hActiveCursor;
788     hActiveCursor = hCursor;
789     /* Change the cursor shape only if it is visible */
790     if (CURSOR_ShowCount >= 0) CURSORICON_SetCursor( hActiveCursor );
791     return hOldCursor;
792 }
793
794
795 /***********************************************************************
796  *           SetCursorPos    (USER.70)
797  */
798 void SetCursorPos( short x, short y )
799 {
800     dprintf_cursor( stddeb, "SetCursorPos: x=%d y=%d\n", x, y );
801     XWarpPointer( display, rootWindow, rootWindow, 0, 0, 0, 0, x, y );
802 }
803
804
805 /***********************************************************************
806  *           ShowCursor    (USER.71)
807  */
808 int ShowCursor( BOOL bShow )
809 {
810     dprintf_cursor( stddeb, "ShowCursor: %d, count=%d\n",
811                     bShow, CURSOR_ShowCount );
812
813     if (bShow)
814     {
815         if (++CURSOR_ShowCount == 0)
816             CURSORICON_SetCursor( hActiveCursor );  /* Show it */
817     }
818     else
819     {
820         if (--CURSOR_ShowCount == -1)
821             CURSORICON_SetCursor( 0 );  /* Hide it */
822     }
823     return CURSOR_ShowCount;
824 }
825
826
827 /***********************************************************************
828  *           GetCursor    (USER.247)
829  */
830 HCURSOR GetCursor(void)
831 {
832     return hActiveCursor;
833 }
834
835
836 /***********************************************************************
837  *           ClipCursor    (USER.16)
838  */
839 void ClipCursor( RECT *rect )
840 {
841     if (!rect) SetRectEmpty( &CURSOR_ClipRect );
842     else CopyRect( &CURSOR_ClipRect, rect );
843 }
844
845
846 /***********************************************************************
847  *           GetCursorPos    (USER.17)
848  */
849 void GetCursorPos( POINT *pt )
850 {
851     Window root, child;
852     int rootX, rootY, childX, childY;
853     unsigned int mousebut;
854
855     if (!pt) return;
856     if (!XQueryPointer( display, rootWindow, &root, &child,
857                         &rootX, &rootY, &childX, &childY, &mousebut ))
858         pt->x = pt->y = 0;
859     else
860     {
861         pt->x = rootX + desktopX;
862         pt->y = rootY + desktopY;
863     }
864     dprintf_cursor(stddeb, "GetCursorPos: ret=%ld,%ld\n", (LONG)pt->x, 
865                    (LONG)pt->y );
866 }
867
868
869 /***********************************************************************
870  *           GetClipCursor    (USER.309)
871  */
872 void GetClipCursor( RECT *rect )
873 {
874     if (rect) CopyRect( rect, &CURSOR_ClipRect );
875 }
876
877
878 /**********************************************************************
879  *          GetIconID    (USER.455)
880  */
881 WORD GetIconID( HANDLE hResource, DWORD resType )
882 {
883     fprintf( stderr, "GetIconId("NPFMT",%ld): empty stub!\n",
884              hResource, resType );
885     return 0;
886 }
887
888
889 /**********************************************************************
890  *          LoadIconHandler    (USER.456)
891  */
892 HICON LoadIconHandler( HANDLE hResource, BOOL bNew )
893 {
894     fprintf( stderr, "LoadIconHandle("NPFMT",%d): empty stub!\n",
895              hResource, bNew );
896     return 0;
897 }