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