]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkcursor-win32.c
As there is only one GdkDisplay in the Win32 backend, check that
[~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, FALSE);
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_val_if_fail (display == _gdk_display, FALSE);
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, gint height, guchar **outdata)
617 {
618   BITMAPV4HEADER bi;
619   HDC hdc;
620   HBITMAP hBitmap;
621
622   ZeroMemory (&bi, sizeof (BITMAPV4HEADER));
623   bi.bV4Size = sizeof (BITMAPV4HEADER);
624   bi.bV4Width = width;
625   bi.bV4Height = height;
626   bi.bV4Planes = 1;
627   bi.bV4BitCount = 24;
628   bi.bV4V4Compression = BI_RGB;
629
630   hdc = GetDC (NULL);
631   if (!hdc)
632     {
633       WIN32_GDI_FAILED ("GetDC");
634       return NULL;
635     }
636   hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
637                               (PVOID *) outdata, NULL, (DWORD)0);
638   if (hBitmap == NULL)
639     WIN32_GDI_FAILED ("CreateDIBSection");
640   ReleaseDC (NULL, hdc);
641
642   return hBitmap;
643 }
644
645 static gboolean
646 pixbuf_to_hbitmaps_alpha_winxp (GdkPixbuf *pixbuf,
647                                 HBITMAP   *color,
648                                 HBITMAP   *mask)
649 {
650   /* Based on code from
651    * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
652    */
653   HBITMAP hColorBitmap, hMaskBitmap;
654   guchar *indata, *inrow, *maskdata, *maskrow;
655   guchar *colordata, *colorrow;
656   gint width, height, i, j, rowstride, bmstride;
657
658   width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
659   height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
660
661   hColorBitmap = create_alpha_bitmap (width, height, &colordata);
662   if (!hColorBitmap)
663     return FALSE;
664   hMaskBitmap = create_color_bitmap (width, height, &maskdata);
665   if (!hMaskBitmap)
666     {
667       DeleteObject (hColorBitmap);
668       return FALSE;
669     }
670
671   bmstride = width * 3;
672   if (bmstride % 4 != 0)
673     bmstride += 4 - (bmstride % 4);
674
675   indata = gdk_pixbuf_get_pixels (pixbuf);
676   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
677   for (j=0; j<height; j++)
678     {
679       colorrow = colordata + 4*j*width;
680       maskrow = maskdata + j*bmstride;
681       inrow = indata  + (height-j-1)*rowstride;
682       for (i=0; i<width; i++)
683         {
684           colorrow[4*i+0] = inrow[4*i+2];
685           colorrow[4*i+1] = inrow[4*i+1];
686           colorrow[4*i+2] = inrow[4*i+0];
687           colorrow[4*i+3] = inrow[4*i+3];
688           if (inrow[4*i+3] == 0)
689             maskrow[3*i+0] = maskrow[3*i+1] = maskrow[3*i+2] = 255;
690           else
691             maskrow[3*i+0] = maskrow[3*i+1] = maskrow[3*i+2] = 0;
692         }
693     }
694
695   *color = hColorBitmap;
696   *mask = hMaskBitmap;
697
698   return TRUE;
699 }
700
701 static gboolean
702 pixbuf_to_hbitmaps_normal (GdkPixbuf *pixbuf,
703                            HBITMAP   *color,
704                            HBITMAP   *mask)
705 {
706   /* Based on code from
707    * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
708    */
709   HBITMAP hColorBitmap, hMaskBitmap;
710   guchar *indata, *inrow;
711   guchar *colordata, *colorrow, *maskdata, *maskrow;
712   gint width, height, i, j, rowstride, nc, bmstride;
713   gboolean has_alpha;
714
715   width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
716   height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
717
718   hColorBitmap = create_color_bitmap (width, height, &colordata);
719   if (!hColorBitmap)
720     return FALSE;
721   hMaskBitmap  = create_color_bitmap (width, height, &maskdata);
722   if (!hMaskBitmap)
723     {
724       DeleteObject (hColorBitmap);
725       return FALSE;
726     }
727
728   /* rows are always aligned on 4-byte boundarys */
729   bmstride = width * 3;
730   if (bmstride % 4 != 0)
731     bmstride += 4 - (bmstride % 4);
732
733   indata = gdk_pixbuf_get_pixels (pixbuf);
734   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
735   nc = gdk_pixbuf_get_n_channels (pixbuf);
736   has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
737
738   for (j=0; j<height; j++)
739     {
740       colorrow = colordata + j*bmstride;
741       maskrow = maskdata + j*bmstride;
742       inrow = indata  + (height-j-1)*rowstride;
743       for (i=0; i<width; i++)
744         {
745           if (has_alpha && inrow[nc*i+3] < 128)
746             {
747               colorrow[3*i+0] = colorrow[3*i+1] = colorrow[3*i+2] = 0;
748               maskrow[3*i+0] = maskrow[3*i+1] = maskrow[3*i+2] = 255;
749             }
750           else
751             {
752               colorrow[3*i+0] = inrow[nc*i+2];
753               colorrow[3*i+1] = inrow[nc*i+1];
754               colorrow[3*i+2] = inrow[nc*i+0];
755               maskrow[3*i+0] = maskrow[3*i+1] = maskrow[3*i+2] = 0;
756             }
757         }
758     }
759
760   *color = hColorBitmap;
761   *mask = hMaskBitmap;
762
763   return TRUE;
764 }
765
766 static HICON
767 pixbuf_to_hicon (GdkPixbuf *pixbuf,
768                  gboolean   is_icon,
769                  gint       x,
770                  gint       y)
771 {
772   ICONINFO ii;
773   HICON icon;
774   gboolean success;
775
776   if (pixbuf == NULL)
777     return NULL;
778
779   if (_gdk_win32_pixbuf_to_hicon_supports_alpha() && gdk_pixbuf_get_has_alpha (pixbuf))
780     success = pixbuf_to_hbitmaps_alpha_winxp (pixbuf, &ii.hbmColor, &ii.hbmMask);
781   else
782     success = pixbuf_to_hbitmaps_normal (pixbuf, &ii.hbmColor, &ii.hbmMask);
783
784   if (!success)
785     return NULL;
786
787   ii.fIcon = is_icon;
788   ii.xHotspot = x;
789   ii.yHotspot = y;
790   icon = CreateIconIndirect (&ii);
791   DeleteObject (ii.hbmColor);
792   DeleteObject (ii.hbmMask);
793   return icon;
794 }
795
796 HICON
797 _gdk_win32_pixbuf_to_hicon (GdkPixbuf *pixbuf)
798 {
799   return pixbuf_to_hicon (pixbuf, TRUE, 0, 0);
800 }
801
802 HICON
803 _gdk_win32_pixbuf_to_hcursor (GdkPixbuf *pixbuf,
804                               gint       x_hotspot,
805                               gint       y_hotspot)
806 {
807   return pixbuf_to_hicon (pixbuf, FALSE, x_hotspot, y_hotspot);
808 }
809
810 gboolean
811 _gdk_win32_pixbuf_to_hicon_supports_alpha (void)
812 {
813   static gboolean is_win_xp=FALSE, is_win_xp_checked=FALSE;
814
815   if (!is_win_xp_checked)
816     {
817       OSVERSIONINFO version;
818
819       is_win_xp_checked = TRUE;
820       memset (&version, 0, sizeof (version));
821       version.dwOSVersionInfoSize = sizeof (version);
822       is_win_xp = GetVersionEx (&version)
823         && version.dwPlatformId == VER_PLATFORM_WIN32_NT
824         && (version.dwMajorVersion > 5
825             || (version.dwMajorVersion == 5 && version.dwMinorVersion >= 1));
826     }
827   return is_win_xp;
828 }
829
830 HICON
831 gdk_win32_pixbuf_to_hicon_libgtk_only (GdkPixbuf *pixbuf)
832 {
833   return _gdk_win32_pixbuf_to_hicon (pixbuf);
834 }