]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkcursor-win32.c
Include "config.h" instead of <config.h> Command used: find -name
[~andy/gtk] / gdk / win32 / gdkcursor-win32.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998-2002 Tor Lillqvist
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #define GDK_PIXBUF_ENABLE_BACKEND /* Ugly? */
23 #include "gdkdisplay.h"
24 #include "gdkscreen.h"
25 #include "gdkcursor.h"
26 #include "gdkprivate-win32.h"
27
28 #ifdef __MINGW32__
29 #include <w32api.h>
30 #endif
31
32 #include "xcursors.h"
33
34 #if (defined(__MINGW32__) && (__W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 8))) || (defined(_MSC_VER) && (WINVER < 0x0500))
35 typedef struct { 
36   DWORD        bV5Size; 
37   LONG         bV5Width; 
38   LONG         bV5Height; 
39   WORD         bV5Planes; 
40   WORD         bV5BitCount; 
41   DWORD        bV5Compression; 
42   DWORD        bV5SizeImage; 
43   LONG         bV5XPelsPerMeter; 
44   LONG         bV5YPelsPerMeter; 
45   DWORD        bV5ClrUsed; 
46   DWORD        bV5ClrImportant; 
47   DWORD        bV5RedMask; 
48   DWORD        bV5GreenMask; 
49   DWORD        bV5BlueMask; 
50   DWORD        bV5AlphaMask; 
51   DWORD        bV5CSType; 
52   CIEXYZTRIPLE bV5Endpoints; 
53   DWORD        bV5GammaRed; 
54   DWORD        bV5GammaGreen; 
55   DWORD        bV5GammaBlue; 
56   DWORD        bV5Intent; 
57   DWORD        bV5ProfileData; 
58   DWORD        bV5ProfileSize; 
59   DWORD        bV5Reserved; 
60 } BITMAPV5HEADER;
61 #endif
62
63 static HCURSOR
64 _gdk_win32_data_to_wcursor (GdkCursorType cursor_type)
65 {
66   gint i, j, x, y, ofs;
67   HCURSOR rv = NULL;
68   gint w, h;
69   guchar *and_plane, *xor_plane;
70
71   for (i = 0; i < G_N_ELEMENTS (cursors); i++)
72     if (cursors[i].type == cursor_type)
73       break;
74
75   if (i >= G_N_ELEMENTS (cursors) || !cursors[i].name)
76     return NULL;
77
78   /* use real win32 cursor if possible */
79   if (cursors[i].builtin)
80     {
81       return LoadCursor (NULL, cursors[i].builtin);
82     }
83
84   w = GetSystemMetrics (SM_CXCURSOR);
85   h = GetSystemMetrics (SM_CYCURSOR);
86
87   and_plane = g_malloc ((w/8) * h);
88   memset (and_plane, 0xff, (w/8) * h);
89   xor_plane = g_malloc ((w/8) * h);
90   memset (xor_plane, 0, (w/8) * h);
91
92 #define SET_BIT(v,b)  (v |= (1 << b))
93 #define RESET_BIT(v,b)  (v &= ~(1 << b))
94
95   for (j = 0, y = 0; y < cursors[i].height && y < h ; y++)
96     {
97       ofs = (y * w) / 8;
98       j = y * cursors[i].width;
99
100       for (x = 0; x < cursors[i].width && x < w ; x++, j++)
101       {
102         gint pofs = ofs + x / 8;
103         guchar data = (cursors[i].data[j/4] & (0xc0 >> (2 * (j%4)))) >> (2 * (3 - (j%4)));
104         gint bit = 7 - (j % cursors[i].width) % 8;
105
106         if (data)
107           {
108             RESET_BIT (and_plane[pofs], bit);
109             if (data == 1)
110               SET_BIT (xor_plane[pofs], bit);
111           }
112       }
113     }
114
115 #undef SET_BIT
116 #undef RESET_BIT
117
118   rv = CreateCursor (_gdk_app_hmodule, cursors[i].hotx, cursors[i].hoty,
119                      w, h, and_plane, xor_plane);
120   if (rv == NULL)
121     WIN32_API_FAILED ("CreateCursor");
122   g_free (and_plane);
123   g_free (xor_plane);
124   
125   return rv;
126 }
127
128 static GdkCursor*
129 _gdk_win32_cursor_new_from_hcursor (HCURSOR hcursor, GdkCursorType cursor_type)
130 {
131   GdkCursorPrivate *private;
132   GdkCursor *cursor;
133
134   private = g_new (GdkCursorPrivate, 1);
135   private->hcursor = hcursor;
136   cursor = (GdkCursor*) private;
137   cursor->type = cursor_type;
138   cursor->ref_count = 1;
139
140   return cursor;
141 }
142
143 GdkCursor*
144 gdk_cursor_new_for_display (GdkDisplay   *display,
145                             GdkCursorType cursor_type)
146 {
147   HCURSOR hcursor;
148
149   g_return_val_if_fail (display == _gdk_display, NULL);
150
151   hcursor = _gdk_win32_data_to_wcursor (cursor_type);
152
153   if (hcursor == NULL)
154     g_warning ("gdk_cursor_new_for_display: no cursor %d found", cursor_type);
155   else
156     GDK_NOTE (MISC, g_print ("gdk_cursor_new_for_display: %d: %p\n",
157                              cursor_type, hcursor));
158
159   return _gdk_win32_cursor_new_from_hcursor (hcursor, cursor_type);
160 }
161
162 static gboolean
163 color_is_white (const GdkColor *color)
164 {
165   return (color->red == 0xFFFF
166           && color->green == 0xFFFF
167           && color->blue == 0xFFFF);
168 }
169
170 GdkCursor*
171 gdk_cursor_new_from_pixmap (GdkPixmap      *source,
172                             GdkPixmap      *mask,
173                             const GdkColor *fg,
174                             const GdkColor *bg,
175                             gint            x,
176                             gint            y)
177 {
178   GdkPixmapImplWin32 *source_impl, *mask_impl;
179   guchar *source_bits, *mask_bits;
180   gint source_bpl, mask_bpl;
181   HCURSOR hcursor;
182   guchar *p, *q, *xor_mask, *and_mask;
183   gint width, height, cursor_width, cursor_height;
184   guchar residue;
185   gint ix, iy;
186   const gboolean bg_is_white = color_is_white (bg);
187   
188   g_return_val_if_fail (GDK_IS_PIXMAP (source), NULL);
189   g_return_val_if_fail (GDK_IS_PIXMAP (mask), NULL);
190   g_return_val_if_fail (fg != NULL, NULL);
191   g_return_val_if_fail (bg != NULL, NULL);
192
193   source_impl = GDK_PIXMAP_IMPL_WIN32 (GDK_PIXMAP_OBJECT (source)->impl);
194   mask_impl = GDK_PIXMAP_IMPL_WIN32 (GDK_PIXMAP_OBJECT (mask)->impl);
195
196   g_return_val_if_fail (source_impl->width == mask_impl->width
197                         && source_impl->height == mask_impl->height,
198                         NULL);
199   width = source_impl->width;
200   height = source_impl->height;
201   cursor_width = GetSystemMetrics (SM_CXCURSOR);
202   cursor_height = GetSystemMetrics (SM_CYCURSOR);
203
204   g_return_val_if_fail (width <= cursor_width && height <= cursor_height,
205                         NULL);
206
207   residue = (1 << ((8-(width%8))%8)) - 1;
208
209   source_bits = source_impl->bits;
210   mask_bits = mask_impl->bits;
211
212   g_return_val_if_fail (GDK_PIXMAP_OBJECT (source)->depth == 1
213                         && GDK_PIXMAP_OBJECT (mask)->depth == 1,
214                         NULL);
215
216   source_bpl = ((width - 1)/32 + 1)*4;
217   mask_bpl = ((mask_impl->width - 1)/32 + 1)*4;
218
219 #ifdef G_ENABLE_DEBUG
220   if (_gdk_debug_flags & GDK_DEBUG_CURSOR)
221     {
222       g_print ("gdk_cursor_new_from_pixmap: source=%p:\n",
223                source_impl->parent_instance.handle);
224       for (iy = 0; iy < height; iy++)
225         {
226           if (iy == 16)
227             break;
228
229           p = source_bits + iy*source_bpl;
230           for (ix = 0; ix < width; ix++)
231             {
232               if (ix == 79)
233                 break;
234               g_print ("%c", ".X"[((*p)>>(7-(ix%8)))&1]);
235               if ((ix%8) == 7)
236                 p++;
237             }
238           g_print ("\n");
239         }
240       g_print ("...mask=%p:\n", mask_impl->parent_instance.handle);
241       for (iy = 0; iy < height; iy++)
242         {
243           if (iy == 16)
244             break;
245
246           p = mask_bits + iy*source_bpl;
247           for (ix = 0; ix < width; ix++)
248             {
249               if (ix == 79)
250                 break;
251               g_print ("%c", ".X"[((*p)>>(7-(ix%8)))&1]);
252               if ((ix%8) == 7)
253                 p++;
254             }
255           g_print ("\n");
256         }
257     }
258 #endif
259
260   /* Such complex bit manipulation for this simple task, sigh.
261    * The X cursor and Windows cursor concepts are quite different.
262    * We assume here that we are always called with fg == black and
263    * bg == white, *or* the other way around. Random colours won't work.
264    * (Well, you will get a cursor, but not in those colours.)
265    */
266
267   /* Note: The comments below refer to the case fg==black and
268    * bg==white, as that was what was implemented first. The fg==white
269    * (the "if (fg->pixel)" branches) case was added later.
270    */
271
272   /* First set masked-out source bits, as all source bits matter on Windoze.
273    * As we invert them below, they will be clear in the final xor_mask.
274    */
275   for (iy = 0; iy < height; iy++)
276     {
277       p = source_bits + iy*source_bpl;
278       q = mask_bits + iy*mask_bpl;
279       
280       for (ix = 0; ix < ((width-1)/8+1); ix++)
281         if (bg_is_white)
282           *p++ |= ~(*q++);
283         else
284           *p++ &= *q++;
285     }
286
287   /* XOR mask is initialized to zero */
288   xor_mask = g_malloc0 (cursor_width/8 * cursor_height);
289
290   for (iy = 0; iy < height; iy++)
291     {
292       p = source_bits + iy*source_bpl;
293       q = xor_mask + iy*cursor_width/8;
294
295       for (ix = 0; ix < ((width-1)/8+1); ix++)
296         if (bg_is_white)
297           *q++ = ~(*p++);
298         else
299           *q++ = *p++;
300
301       q[-1] &= ~residue;        /* Clear left-over bits */
302     }
303       
304   /* AND mask is initialized to ones */
305   and_mask = g_malloc (cursor_width/8 * cursor_height);
306   memset (and_mask, 0xFF, cursor_width/8 * cursor_height);
307
308   for (iy = 0; iy < height; iy++)
309     {
310       p = mask_bits + iy*mask_bpl;
311       q = and_mask + iy*cursor_width/8;
312
313       for (ix = 0; ix < ((width-1)/8+1); ix++)
314         *q++ = ~(*p++);
315
316       q[-1] |= residue; /* Set left-over bits */
317     }
318       
319   hcursor = CreateCursor (_gdk_app_hmodule, x, y, cursor_width, cursor_height,
320                           and_mask, xor_mask);
321
322   GDK_NOTE (MISC, g_print ("gdk_cursor_new_from_pixmap: "
323                            "%p (%dx%d) %p (%dx%d) = %p (%dx%d)\n",
324                            GDK_PIXMAP_HBITMAP (source),
325                            source_impl->width, source_impl->height,
326                            GDK_PIXMAP_HBITMAP (mask),
327                            mask_impl->width, mask_impl->height,
328                            hcursor, cursor_width, cursor_height));
329
330   g_free (xor_mask);
331   g_free (and_mask);
332
333   return _gdk_win32_cursor_new_from_hcursor (hcursor, GDK_CURSOR_IS_PIXMAP);
334 }
335
336 /* The named cursors below are presumably not really useful, as the
337  * names are Win32-specific. No GTK+ application developed on Unix
338  * (and most cross-platform GTK+ apps are developed on Unix) is going
339  * to look for cursors under these Win32 names anyway.
340  *
341  * Would the following make any sense: The ms-windows theme engine
342  * calls some (to-be-defined private) API here in gdk/win32 to
343  * register the relevant cursors used by the currently active XP
344  * visual style under the names that libgtk uses to look for them
345  * ("color-picker", "dnd-ask", "dnd-copy", etc), and then when libgtk
346  * asks for those we return the ones registered by the ms-windows
347  * theme engine, if any.
348  */
349
350 static struct {
351   char *name;
352   char *id;
353 } _default_cursors[] = {
354   { "appstarting", IDC_APPSTARTING },
355   { "arrow", IDC_ARROW },
356   { "cross", IDC_CROSS },
357 #ifdef IDC_HAND
358   { "hand",  IDC_HAND },
359 #endif
360   { "help",  IDC_HELP },
361   { "ibeam", IDC_IBEAM },
362   { "sizeall", IDC_SIZEALL },
363   { "sizenesw", IDC_SIZENESW },
364   { "sizens", IDC_SIZENS },
365   { "sizenwse", IDC_SIZENWSE },
366   { "sizewe", IDC_SIZEWE },
367   { "uparrow", IDC_UPARROW },
368   { "wait", IDC_WAIT }
369 };
370
371 GdkCursor*  
372 gdk_cursor_new_from_name (GdkDisplay  *display,
373                           const gchar *name)
374 {
375   HCURSOR hcursor = NULL;
376   int i;
377
378   g_return_val_if_fail (display == _gdk_display, NULL);
379
380   for (i = 0; i < G_N_ELEMENTS(_default_cursors); i++)
381     {
382       if (0 == strcmp(_default_cursors[i].name, name))
383         hcursor = LoadCursor (NULL, _default_cursors[i].id);
384     }
385   /* allow to load named cursor resources linked into the executable */
386   if (!hcursor)
387     hcursor = LoadCursor (_gdk_app_hmodule, name);
388
389   if (hcursor)
390     return _gdk_win32_cursor_new_from_hcursor (hcursor, GDK_X_CURSOR);
391
392   return NULL;
393 }
394
395 void
396 _gdk_cursor_destroy (GdkCursor *cursor)
397 {
398   GdkCursorPrivate *private;
399
400   g_return_if_fail (cursor != NULL);
401   private = (GdkCursorPrivate *) cursor;
402
403   GDK_NOTE (MISC, g_print ("_gdk_cursor_destroy: %p\n",
404                            (cursor->type == GDK_CURSOR_IS_PIXMAP) ? private->hcursor : 0));
405
406   if (GetCursor () == private->hcursor)
407     SetCursor (NULL);
408
409   if (!DestroyCursor (private->hcursor))
410     WIN32_API_FAILED ("DestroyCursor");
411
412   g_free (private);
413 }
414
415 GdkDisplay *
416 gdk_cursor_get_display (GdkCursor *cursor)
417 {
418   return gdk_display_get_default ();
419 }
420
421 GdkPixbuf *
422 gdk_win32_icon_to_pixbuf_libgtk_only (HICON hicon)
423 {
424   GdkPixbuf *pixbuf = NULL;
425   ICONINFO ii;
426   struct
427   {
428     BITMAPINFOHEADER bi;
429     RGBQUAD colors[2];
430   } bmi;
431   HDC hdc;
432   gchar *pixels, *bits, buf[32];
433   gint rowstride, x, y, w, h;
434
435   if (!GDI_CALL (GetIconInfo, (hicon, &ii)))
436     return NULL;
437
438   if (!(hdc = CreateCompatibleDC (NULL)))
439     {
440       WIN32_GDI_FAILED ("CreateCompatibleDC");
441       goto out0;
442     }
443
444   memset (&bmi, 0, sizeof (bmi));
445   bmi.bi.biSize = sizeof (bmi.bi);
446
447   if (ii.hbmColor != NULL)
448     {
449       /* Colour cursor */
450
451       gboolean no_alpha;
452       
453       if (!GDI_CALL (GetDIBits, (hdc, ii.hbmColor, 0, 1, NULL, (BITMAPINFO *)&bmi, DIB_RGB_COLORS)))
454         goto out1;
455
456       w = bmi.bi.biWidth;
457       h = bmi.bi.biHeight;
458
459       bmi.bi.biBitCount = 32;
460       bmi.bi.biCompression = BI_RGB;
461       bmi.bi.biHeight = -h;
462
463       bits = g_malloc0 (4 * w * h);
464       
465       /* color data */
466       if (!GDI_CALL (GetDIBits, (hdc, ii.hbmColor, 0, h, bits, (BITMAPINFO *)&bmi, DIB_RGB_COLORS)))
467         goto out2;
468
469       pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, w, h);
470       pixels = gdk_pixbuf_get_pixels (pixbuf);
471       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
472       no_alpha = TRUE;
473       for (y = 0; y < h; y++)
474         {
475           for (x = 0; x < w; x++)
476             {
477               pixels[2] = bits[(x+y*w) * 4];
478               pixels[1] = bits[(x+y*w) * 4 + 1];
479               pixels[0] = bits[(x+y*w) * 4 + 2];
480               pixels[3] = bits[(x+y*w) * 4 + 3];
481               if (no_alpha && pixels[3] > 0)
482                 no_alpha = FALSE;
483               pixels += 4;
484             }
485           pixels += (w * 4 - rowstride);
486         }
487
488       /* mask */
489       if (no_alpha &&
490           GDI_CALL (GetDIBits, (hdc, ii.hbmMask, 0, h, bits, (BITMAPINFO *)&bmi, DIB_RGB_COLORS)))
491         {
492           pixels = gdk_pixbuf_get_pixels (pixbuf);
493           for (y = 0; y < h; y++)
494             {
495               for (x = 0; x < w; x++)
496                 {
497                   pixels[3] = 255 - bits[(x + y * w) * 4];
498                   pixels += 4;
499                 }
500               pixels += (w * 4 - rowstride);
501             }
502         }
503     }
504   else
505     {
506       /* B&W cursor */
507
508       int bpl;
509
510       if (!GDI_CALL (GetDIBits, (hdc, ii.hbmMask, 0, 0, NULL, (BITMAPINFO *)&bmi, DIB_RGB_COLORS)))
511         goto out1;
512
513       w = bmi.bi.biWidth;
514       h = ABS (bmi.bi.biHeight) / 2;
515       
516       bits = g_malloc0 (4 * w * h);
517       
518       /* masks */
519       if (!GDI_CALL (GetDIBits, (hdc, ii.hbmMask, 0, h*2, bits, (BITMAPINFO *)&bmi, DIB_RGB_COLORS)))
520         goto out2;
521
522       pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, w, h);
523       pixels = gdk_pixbuf_get_pixels (pixbuf);
524       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
525       bpl = ((w-1)/32 + 1)*4;
526 #if 0
527       for (y = 0; y < h*2; y++)
528         {
529           for (x = 0; x < w; x++)
530             {
531               const gint bit = 7 - (x % 8);
532               printf ("%c ", ((bits[bpl*y+x/8])&(1<<bit)) ? ' ' : 'X');
533             }
534           printf ("\n");
535         }
536 #endif
537
538       for (y = 0; y < h; y++)
539         {
540           const guchar *andp, *xorp;
541           if (bmi.bi.biHeight < 0)
542             {
543               andp = bits + bpl*y;
544               xorp = bits + bpl*(h+y);
545             }
546           else
547             {
548               andp = bits + bpl*(h-y-1);
549               xorp = bits + bpl*(h+h-y-1);
550             }
551           for (x = 0; x < w; x++)
552             {
553               const gint bit = 7 - (x % 8);
554               if ((*andp) & (1<<bit))
555                 {
556                   if ((*xorp) & (1<<bit))
557                     pixels[2] = pixels[1] = pixels[0] = 0xFF;
558                   else
559                     pixels[2] = pixels[1] = pixels[0] = 0;
560                   pixels[3] = 0xFF;
561                 }
562               else
563                 {
564                   pixels[2] = pixels[1] = pixels[0] = 0;
565                   pixels[3] = 0;
566                 }
567               pixels += 4;
568               if (bit == 0)
569                 {
570                   andp++;
571                   xorp++;
572                 }
573             }
574           pixels += (w * 4 - rowstride);
575         }
576     }
577
578   g_snprintf (buf, sizeof (buf), "%ld", ii.xHotspot);
579   gdk_pixbuf_set_option (pixbuf, "x_hot", buf);
580
581   g_snprintf (buf, sizeof (buf), "%ld", ii.yHotspot);
582   gdk_pixbuf_set_option (pixbuf, "y_hot", buf);
583
584   /* release temporary resources */
585  out2:
586   g_free (bits);
587  out1:
588   DeleteDC (hdc);
589  out0:
590   DeleteObject (ii.hbmColor);
591   DeleteObject (ii.hbmMask);
592
593   return pixbuf;
594 }
595
596 GdkPixbuf*  
597 gdk_cursor_get_image (GdkCursor *cursor)
598 {
599   g_return_val_if_fail (cursor != NULL, NULL);
600
601   return gdk_win32_icon_to_pixbuf_libgtk_only (((GdkCursorPrivate *) cursor)->hcursor);
602 }
603
604 GdkCursor *
605 gdk_cursor_new_from_pixbuf (GdkDisplay *display, 
606                             GdkPixbuf  *pixbuf,
607                             gint        x,
608                             gint        y)
609 {
610   HCURSOR hcursor;
611
612   g_return_val_if_fail (display == _gdk_display, NULL);
613   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
614   g_return_val_if_fail (0 <= x && x < gdk_pixbuf_get_width (pixbuf), NULL);
615   g_return_val_if_fail (0 <= y && y < gdk_pixbuf_get_height (pixbuf), NULL);
616
617   hcursor = _gdk_win32_pixbuf_to_hcursor (pixbuf, x, y);
618   if (!hcursor)
619     return NULL;
620   return _gdk_win32_cursor_new_from_hcursor (hcursor, GDK_CURSOR_IS_PIXMAP);
621 }
622
623 gboolean 
624 gdk_display_supports_cursor_alpha (GdkDisplay    *display)
625 {
626   g_return_val_if_fail (display == _gdk_display, FALSE);
627
628   return _gdk_win32_pixbuf_to_hicon_supports_alpha ();
629 }
630
631 gboolean 
632 gdk_display_supports_cursor_color (GdkDisplay    *display)
633 {
634   g_return_val_if_fail (display == _gdk_display, FALSE);
635
636   return TRUE;
637 }
638
639 guint     
640 gdk_display_get_default_cursor_size (GdkDisplay    *display)
641 {
642   g_return_val_if_fail (display == _gdk_display, 0);
643   
644   return MIN (GetSystemMetrics (SM_CXCURSOR), GetSystemMetrics (SM_CYCURSOR));
645 }
646
647 void     
648 gdk_display_get_maximal_cursor_size (GdkDisplay *display,
649                                      guint       *width,
650                                      guint       *height)
651 {
652   g_return_if_fail (display == _gdk_display);
653   
654   if (width)
655     *width = GetSystemMetrics (SM_CXCURSOR);
656   if (height)
657     *height = GetSystemMetrics (SM_CYCURSOR);
658 }
659
660
661 /* Convert a pixbuf to an HICON (or HCURSOR).  Supports alpha under
662  * Windows XP, thresholds alpha otherwise.  Also used from
663  * gdkwindow-win32.c for creating application icons.
664  */
665
666 static HBITMAP
667 create_alpha_bitmap (gint     size,
668                      guchar **outdata)
669 {
670   BITMAPV5HEADER bi;
671   HDC hdc;
672   HBITMAP hBitmap;
673
674   ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
675   bi.bV5Size = sizeof (BITMAPV5HEADER);
676   bi.bV5Height = bi.bV5Width = size;
677   bi.bV5Planes = 1;
678   bi.bV5BitCount = 32;
679   bi.bV5Compression = BI_BITFIELDS;
680   /* The following mask specification specifies a supported 32 BPP
681    * alpha format for Windows XP (BGRA format).
682    */
683   bi.bV5RedMask   = 0x00FF0000;
684   bi.bV5GreenMask = 0x0000FF00;
685   bi.bV5BlueMask  = 0x000000FF;
686   bi.bV5AlphaMask = 0xFF000000;
687
688   /* Create the DIB section with an alpha channel. */
689   hdc = GetDC (NULL);
690   if (!hdc)
691     {
692       WIN32_GDI_FAILED ("GetDC");
693       return NULL;
694     }
695   hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
696                               (PVOID *) outdata, NULL, (DWORD)0);
697   if (hBitmap == NULL)
698     WIN32_GDI_FAILED ("CreateDIBSection");
699   ReleaseDC (NULL, hdc);
700
701   return hBitmap;
702 }
703
704 static HBITMAP
705 create_color_bitmap (gint     size,
706                      guchar **outdata,
707                      gint     bits)
708 {
709   struct {
710     BITMAPV4HEADER bmiHeader;
711     RGBQUAD bmiColors[2];
712   } bmi;
713   HDC hdc;
714   HBITMAP hBitmap;
715
716   ZeroMemory (&bmi, sizeof (bmi));
717   bmi.bmiHeader.bV4Size = sizeof (BITMAPV4HEADER);
718   bmi.bmiHeader.bV4Height = bmi.bmiHeader.bV4Width = size;
719   bmi.bmiHeader.bV4Planes = 1;
720   bmi.bmiHeader.bV4BitCount = bits;
721   bmi.bmiHeader.bV4V4Compression = BI_RGB;
722
723   /* when bits is 1, these will be used.
724    * bmiColors[0] already zeroed from ZeroMemory()
725    */
726   bmi.bmiColors[1].rgbBlue = 0xFF;
727   bmi.bmiColors[1].rgbGreen = 0xFF;
728   bmi.bmiColors[1].rgbRed = 0xFF;
729
730   hdc = GetDC (NULL);
731   if (!hdc)
732     {
733       WIN32_GDI_FAILED ("GetDC");
734       return NULL;
735     }
736   hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bmi, DIB_RGB_COLORS,
737                               (PVOID *) outdata, NULL, (DWORD)0);
738   if (hBitmap == NULL)
739     WIN32_GDI_FAILED ("CreateDIBSection");
740   ReleaseDC (NULL, hdc);
741
742   return hBitmap;
743 }
744
745 static gboolean
746 pixbuf_to_hbitmaps_alpha_winxp (GdkPixbuf *pixbuf,
747                                 HBITMAP   *color,
748                                 HBITMAP   *mask)
749 {
750   /* Based on code from
751    * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
752    */
753   HBITMAP hColorBitmap, hMaskBitmap;
754   guchar *indata, *inrow;
755   guchar *colordata, *colorrow, *maskdata, *maskbyte;
756   gint width, height, size, i, i_offset, j, j_offset, rowstride;
757   guint maskstride, mask_bit;
758
759   width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
760   height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
761
762   /* The bitmaps are created square */
763   size = MAX (width, height);
764
765   hColorBitmap = create_alpha_bitmap (size, &colordata);
766   if (!hColorBitmap)
767     return FALSE;
768   hMaskBitmap = create_color_bitmap (size, &maskdata, 1);
769   if (!hMaskBitmap)
770     {
771       DeleteObject (hColorBitmap);
772       return FALSE;
773     }
774
775   /* MSDN says mask rows are aligned to "LONG" boundaries */
776   maskstride = (((size + 31) & ~31) >> 3);
777
778   indata = gdk_pixbuf_get_pixels (pixbuf);
779   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
780
781   if (width > height)
782     {
783       i_offset = 0;
784       j_offset = (width - height) / 2;
785     }
786   else
787     {
788       i_offset = (height - width) / 2;
789       j_offset = 0;
790     }
791
792   for (j = 0; j < height; j++)
793     {
794       colorrow = colordata + 4*(j+j_offset)*size + 4*i_offset;
795       maskbyte = maskdata + (j+j_offset)*maskstride + i_offset/8;
796       mask_bit = (0x80 >> (i_offset % 8));
797       inrow = indata + (height-j-1)*rowstride;
798       for (i = 0; i < width; i++)
799         {
800           colorrow[4*i+0] = inrow[4*i+2];
801           colorrow[4*i+1] = inrow[4*i+1];
802           colorrow[4*i+2] = inrow[4*i+0];
803           colorrow[4*i+3] = inrow[4*i+3];
804           if (inrow[4*i+3] == 0)
805             maskbyte[0] |= mask_bit;    /* turn ON bit */
806           else
807             maskbyte[0] &= ~mask_bit;   /* turn OFF bit */
808           mask_bit >>= 1;
809           if (mask_bit == 0)
810             {
811               mask_bit = 0x80;
812               maskbyte++;
813             }
814         }
815     }
816
817   *color = hColorBitmap;
818   *mask = hMaskBitmap;
819
820   return TRUE;
821 }
822
823 static gboolean
824 pixbuf_to_hbitmaps_normal (GdkPixbuf *pixbuf,
825                            HBITMAP   *color,
826                            HBITMAP   *mask)
827 {
828   /* Based on code from
829    * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
830    */
831   HBITMAP hColorBitmap, hMaskBitmap;
832   guchar *indata, *inrow;
833   guchar *colordata, *colorrow, *maskdata, *maskbyte;
834   gint width, height, size, i, i_offset, j, j_offset, rowstride, nc, bmstride;
835   gboolean has_alpha;
836   guint maskstride, mask_bit;
837
838   width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
839   height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
840
841   /* The bitmaps are created square */
842   size = MAX (width, height);
843
844   hColorBitmap = create_color_bitmap (size, &colordata, 24);
845   if (!hColorBitmap)
846     return FALSE;
847   hMaskBitmap = create_color_bitmap (size, &maskdata, 1);
848   if (!hMaskBitmap)
849     {
850       DeleteObject (hColorBitmap);
851       return FALSE;
852     }
853
854   /* rows are always aligned on 4-byte boundarys */
855   bmstride = size * 3;
856   if (bmstride % 4 != 0)
857     bmstride += 4 - (bmstride % 4);
858
859   /* MSDN says mask rows are aligned to "LONG" boundaries */
860   maskstride = (((size + 31) & ~31) >> 3);
861
862   indata = gdk_pixbuf_get_pixels (pixbuf);
863   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
864   nc = gdk_pixbuf_get_n_channels (pixbuf);
865   has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
866
867   if (width > height)
868     {
869       i_offset = 0;
870       j_offset = (width - height) / 2;
871     }
872   else
873     {
874       i_offset = (height - width) / 2;
875       j_offset = 0;
876     }
877
878   for (j = 0; j < height; j++)
879     {
880       colorrow = colordata + (j+j_offset)*bmstride + 3*i_offset;
881       maskbyte = maskdata + (j+j_offset)*maskstride + i_offset/8;
882       mask_bit = (0x80 >> (i_offset % 8));
883       inrow = indata + (height-j-1)*rowstride;
884       for (i = 0; i < width; i++)
885         {
886           if (has_alpha && inrow[nc*i+3] < 128)
887             {
888               colorrow[3*i+0] = colorrow[3*i+1] = colorrow[3*i+2] = 0;
889               maskbyte[0] |= mask_bit;  /* turn ON bit */
890             }
891           else
892             {
893               colorrow[3*i+0] = inrow[nc*i+2];
894               colorrow[3*i+1] = inrow[nc*i+1];
895               colorrow[3*i+2] = inrow[nc*i+0];
896               maskbyte[0] &= ~mask_bit; /* turn OFF bit */
897             }
898           mask_bit >>= 1;
899           if (mask_bit == 0)
900             {
901               mask_bit = 0x80;
902               maskbyte++;
903             }
904         }
905     }
906
907   *color = hColorBitmap;
908   *mask = hMaskBitmap;
909
910   return TRUE;
911 }
912
913 static HICON
914 pixbuf_to_hicon (GdkPixbuf *pixbuf,
915                  gboolean   is_icon,
916                  gint       x,
917                  gint       y)
918 {
919   ICONINFO ii;
920   HICON icon;
921   gboolean success;
922
923   if (pixbuf == NULL)
924     return NULL;
925
926   if (_gdk_win32_pixbuf_to_hicon_supports_alpha() && gdk_pixbuf_get_has_alpha (pixbuf))
927     success = pixbuf_to_hbitmaps_alpha_winxp (pixbuf, &ii.hbmColor, &ii.hbmMask);
928   else
929     success = pixbuf_to_hbitmaps_normal (pixbuf, &ii.hbmColor, &ii.hbmMask);
930
931   if (!success)
932     return NULL;
933
934   ii.fIcon = is_icon;
935   ii.xHotspot = x;
936   ii.yHotspot = y;
937   icon = CreateIconIndirect (&ii);
938   DeleteObject (ii.hbmColor);
939   DeleteObject (ii.hbmMask);
940   return icon;
941 }
942
943 HICON
944 _gdk_win32_pixbuf_to_hicon (GdkPixbuf *pixbuf)
945 {
946   return pixbuf_to_hicon (pixbuf, TRUE, 0, 0);
947 }
948
949 HICON
950 _gdk_win32_pixbuf_to_hcursor (GdkPixbuf *pixbuf,
951                               gint       x_hotspot,
952                               gint       y_hotspot)
953 {
954   return pixbuf_to_hicon (pixbuf, FALSE, x_hotspot, y_hotspot);
955 }
956
957 gboolean
958 _gdk_win32_pixbuf_to_hicon_supports_alpha (void)
959 {
960   static gboolean is_win_xp=FALSE, is_win_xp_checked=FALSE;
961
962   if (!is_win_xp_checked)
963     {
964       OSVERSIONINFO version;
965
966       is_win_xp_checked = TRUE;
967
968       memset (&version, 0, sizeof (version));
969       version.dwOSVersionInfoSize = sizeof (version);
970       is_win_xp = GetVersionEx (&version)
971         && version.dwPlatformId == VER_PLATFORM_WIN32_NT
972         && (version.dwMajorVersion > 5
973             || (version.dwMajorVersion == 5 && version.dwMinorVersion >= 1));
974     }
975   return is_win_xp;
976 }
977
978 HICON
979 gdk_win32_pixbuf_to_hicon_libgtk_only (GdkPixbuf *pixbuf)
980 {
981   return _gdk_win32_pixbuf_to_hicon (pixbuf);
982 }