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