winmultiwindowicons.c revision 35c4bbdf
1/*
2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *
4 *Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 *"Software"), to deal in the Software without restriction, including
7 *without limitation the rights to use, copy, modify, merge, publish,
8 *distribute, sublicense, and/or sell copies of the Software, and to
9 *permit persons to whom the Software is furnished to do so, subject to
10 *the following conditions:
11 *
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
14 *
15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 *Except as contained in this notice, the name of the XFree86 Project
24 *shall not be used in advertising or otherwise to promote the sale, use
25 *or other dealings in this Software without prior written authorization
26 *from the XFree86 Project.
27 *
28 * Authors:	Earle F. Philhower, III
29 */
30
31#ifdef HAVE_XWIN_CONFIG_H
32#include <xwin-config.h>
33#endif
34
35#ifndef WINVER
36#define WINVER 0x0500
37#endif
38
39#include <X11/Xwindows.h>
40#include <X11/Xlib.h>
41#include <X11/Xutil.h>
42
43#include "winresource.h"
44#include "winprefs.h"
45#include "winmsg.h"
46#include "winmultiwindowicons.h"
47#include "winglobals.h"
48/*
49 * global variables
50 */
51extern HINSTANCE g_hInstance;
52
53/*
54 * Scale an X icon ZPixmap into a Windoze icon bitmap
55 */
56
57static void
58winScaleXImageToWindowsIcon(int iconSize,
59                            int effBPP,
60                            int stride, XImage * pixmap, unsigned char *image)
61{
62    int row, column, effXBPP, effXDepth;
63    unsigned char *outPtr;
64    unsigned char *iconData = 0;
65    int xStride;
66    float factX, factY;
67    int posX, posY;
68    unsigned char *ptr;
69    unsigned int zero;
70    unsigned int color;
71
72    effXBPP = pixmap->bits_per_pixel;
73    if (pixmap->bits_per_pixel == 15)
74        effXBPP = 16;
75
76    effXDepth = pixmap->depth;
77    if (pixmap->depth == 15)
78        effXDepth = 16;
79
80    xStride = pixmap->bytes_per_line;
81    if (stride == 0 || xStride == 0) {
82        ErrorF("winScaleXBitmapToWindows - stride or xStride is zero.  "
83               "Bailing.\n");
84        return;
85    }
86
87    /* Get icon data */
88    iconData = (unsigned char *) pixmap->data;
89
90    /* Keep aspect ratio */
91    factX = ((float) pixmap->width) / ((float) iconSize);
92    factY = ((float) pixmap->height) / ((float) iconSize);
93    if (factX > factY)
94        factY = factX;
95    else
96        factX = factY;
97
98    /* Out-of-bounds, fill icon with zero */
99    zero = 0;
100
101    for (row = 0; row < iconSize; row++) {
102        outPtr = image + stride * row;
103        for (column = 0; column < iconSize; column++) {
104            posX = factX * column;
105            posY = factY * row;
106
107            ptr = (unsigned char *) iconData + posY * xStride;
108            if (effXBPP == 1) {
109                ptr += posX / 8;
110
111                /* Out of X icon bounds, leave space blank */
112                if (posX >= pixmap->width || posY >= pixmap->height)
113                    ptr = (unsigned char *) &zero;
114
115                if ((*ptr) & (1 << (posX & 7)))
116                    switch (effBPP) {
117                    case 32:
118                        *(outPtr++) = 0;
119                    case 24:
120                        *(outPtr++) = 0;
121                    case 16:
122                        *(outPtr++) = 0;
123                    case 8:
124                        *(outPtr++) = 0;
125                        break;
126                    case 1:
127                        outPtr[column / 8] &= ~(1 << (7 - (column & 7)));
128                        break;
129                    }
130                else
131                    switch (effBPP) {
132                    case 32:
133                        *(outPtr++) = 255;
134                        *(outPtr++) = 255;
135                        *(outPtr++) = 255;
136                        *(outPtr++) = 0;
137                        break;
138                    case 24:
139                        *(outPtr++) = 255;
140                    case 16:
141                        *(outPtr++) = 255;
142                    case 8:
143                        *(outPtr++) = 255;
144                        break;
145                    case 1:
146                        outPtr[column / 8] |= (1 << (7 - (column & 7)));
147                        break;
148                    }
149            }
150            else if (effXDepth == 24 || effXDepth == 32) {
151                ptr += posX * (effXBPP / 8);
152
153                /* Out of X icon bounds, leave space blank */
154                if (posX >= pixmap->width || posY >= pixmap->height)
155                    ptr = (unsigned char *) &zero;
156                color = (((*ptr) << 16)
157                         + ((*(ptr + 1)) << 8)
158                         + ((*(ptr + 2)) << 0));
159                switch (effBPP) {
160                case 32:
161                    *(outPtr++) = *(ptr++);     /* b */
162                    *(outPtr++) = *(ptr++);     /* g */
163                    *(outPtr++) = *(ptr++);     /* r */
164                    *(outPtr++) = (effXDepth == 32) ? *(ptr++) : 0x0;   /* alpha */
165                    break;
166                case 24:
167                    *(outPtr++) = *(ptr++);
168                    *(outPtr++) = *(ptr++);
169                    *(outPtr++) = *(ptr++);
170                    break;
171                case 16:
172                    color = ((((*ptr) >> 2) << 10)
173                             + (((*(ptr + 1)) >> 2) << 5)
174                             + (((*(ptr + 2)) >> 2)));
175                    *(outPtr++) = (color >> 8);
176                    *(outPtr++) = (color & 255);
177                    break;
178                case 8:
179                    color = (((*ptr))) + (((*(ptr + 1)))) + (((*(ptr + 2))));
180                    color /= 3;
181                    *(outPtr++) = color;
182                    break;
183                case 1:
184                    if (color)
185                        outPtr[column / 8] |= (1 << (7 - (column & 7)));
186                    else
187                        outPtr[column / 8] &= ~(1 << (7 - (column & 7)));
188                }
189            }
190            else if (effXDepth == 16) {
191                ptr += posX * (effXBPP / 8);
192
193                /* Out of X icon bounds, leave space blank */
194                if (posX >= pixmap->width || posY >= pixmap->height)
195                    ptr = (unsigned char *) &zero;
196                color = ((*ptr) << 8) + (*(ptr + 1));
197                switch (effBPP) {
198                case 32:
199                    *(outPtr++) = (color & 31) << 2;
200                    *(outPtr++) = ((color >> 5) & 31) << 2;
201                    *(outPtr++) = ((color >> 10) & 31) << 2;
202                    *(outPtr++) = 0;    /* resvd */
203                    break;
204                case 24:
205                    *(outPtr++) = (color & 31) << 2;
206                    *(outPtr++) = ((color >> 5) & 31) << 2;
207                    *(outPtr++) = ((color >> 10) & 31) << 2;
208                    break;
209                case 16:
210                    *(outPtr++) = *(ptr++);
211                    *(outPtr++) = *(ptr++);
212                    break;
213                case 8:
214                    *(outPtr++) = (((color & 31)
215                                    + ((color >> 5) & 31)
216                                    + ((color >> 10) & 31)) / 3) << 2;
217                    break;
218                case 1:
219                    if (color)
220                        outPtr[column / 8] |= (1 << (7 - (column & 7)));
221                    else
222                        outPtr[column / 8] &= ~(1 << (7 - (column & 7)));
223                    break;
224                }               /* end switch(effbpp) */
225            }                   /* end if effxbpp==16) */
226        }                       /* end for column */
227    }                           /* end for row */
228}
229
230static HICON
231NetWMToWinIconAlpha(uint32_t * icon)
232{
233    int width = icon[0];
234    int height = icon[1];
235    uint32_t *pixels = &icon[2];
236    HICON result;
237    HDC hdc = GetDC(NULL);
238    uint32_t *DIB_pixels;
239    ICONINFO ii;
240    BITMAPV4HEADER bmh = { sizeof(bmh) };
241
242    /* Define an ARGB pixel format used for Color+Alpha icons */
243    bmh.bV4Width = width;
244    bmh.bV4Height = -height;    /* Invert the image */
245    bmh.bV4Planes = 1;
246    bmh.bV4BitCount = 32;
247    bmh.bV4V4Compression = BI_BITFIELDS;
248    bmh.bV4AlphaMask = 0xFF000000;
249    bmh.bV4RedMask = 0x00FF0000;
250    bmh.bV4GreenMask = 0x0000FF00;
251    bmh.bV4BlueMask = 0x000000FF;
252
253    ii.fIcon = TRUE;
254    ii.xHotspot = 0;            /* ignored */
255    ii.yHotspot = 0;            /* ignored */
256    ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO *) &bmh,
257                                   DIB_RGB_COLORS, (void **) &DIB_pixels, NULL,
258                                   0);
259    ReleaseDC(NULL, hdc);
260
261    if (!ii.hbmColor)
262      return NULL;
263
264    ii.hbmMask = CreateBitmap(width, height, 1, 1, NULL);
265    memcpy(DIB_pixels, pixels, height * width * 4);
266
267    /* CreateIconIndirect() traditionally required DDBitmaps */
268    /* Systems from WinXP accept 32-bit ARGB DIBitmaps with full 8-bit alpha support */
269    /* The icon is created with a DIB + empty DDB mask (an MS example does the same) */
270    result = CreateIconIndirect(&ii);
271
272    DeleteObject(ii.hbmColor);
273    DeleteObject(ii.hbmMask);
274
275    winDebug("NetWMToWinIconAlpha - %d x %d = %p\n", icon[0], icon[1], result);
276    return result;
277}
278
279static HICON
280NetWMToWinIconThreshold(uint32_t * icon)
281{
282    int width = icon[0];
283    int height = icon[1];
284    uint32_t *pixels = &icon[2];
285    int row, col;
286    HICON result;
287    ICONINFO ii;
288
289    HDC hdc = GetDC(NULL);
290    HDC xorDC = CreateCompatibleDC(hdc);
291    HDC andDC = CreateCompatibleDC(hdc);
292
293    ii.fIcon = TRUE;
294    ii.xHotspot = 0;            /* ignored */
295    ii.yHotspot = 0;            /* ignored */
296    ii.hbmColor = CreateCompatibleBitmap(hdc, width, height);
297    ii.hbmMask = CreateCompatibleBitmap(hdc, width, height);
298    ReleaseDC(NULL, hdc);
299    SelectObject(xorDC, ii.hbmColor);
300    SelectObject(andDC, ii.hbmMask);
301
302    for (row = 0; row < height; row++) {
303        for (col = 0; col < width; col++) {
304            if ((*pixels & 0xFF000000) > 31 << 24) {    /* 31 alpha threshold, i.e. opaque above, transparent below */
305                SetPixelV(xorDC, col, row,
306                          RGB(((char *) pixels)[2], ((char *) pixels)[1],
307                              ((char *) pixels)[0]));
308                SetPixelV(andDC, col, row, RGB(0, 0, 0));       /* black mask */
309            }
310            else {
311                SetPixelV(xorDC, col, row, RGB(0, 0, 0));
312                SetPixelV(andDC, col, row, RGB(255, 255, 255)); /* white mask */
313            }
314            pixels++;
315        }
316    }
317    DeleteDC(xorDC);
318    DeleteDC(andDC);
319
320    result = CreateIconIndirect(&ii);
321
322    DeleteObject(ii.hbmColor);
323    DeleteObject(ii.hbmMask);
324
325    winDebug("NetWMToWinIconThreshold - %d x %d = %p\n", icon[0], icon[1],
326             result);
327    return result;
328}
329
330static HICON
331NetWMToWinIcon(int bpp, uint32_t * icon)
332{
333    static Bool hasIconAlphaChannel = FALSE;
334    static BOOL versionChecked = FALSE;
335
336    if (!versionChecked) {
337        OSVERSIONINFOEX osvi = { 0 };
338        ULONGLONG dwlConditionMask = 0;
339
340        osvi.dwOSVersionInfoSize = sizeof(osvi);
341        osvi.dwMajorVersion = 5;
342        osvi.dwMinorVersion = 1;
343
344        /* Windows versions later than XP have icon alpha channel suport, 2000 does not */
345        VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION,
346                          VER_GREATER_EQUAL);
347        VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION,
348                          VER_GREATER_EQUAL);
349        hasIconAlphaChannel =
350            VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION,
351                              dwlConditionMask);
352        versionChecked = TRUE;
353
354        ErrorF("OS has icon alpha channel support: %s\n",
355               hasIconAlphaChannel ? "yes" : "no");
356    }
357
358    if (hasIconAlphaChannel && (bpp == 32))
359        return NetWMToWinIconAlpha(icon);
360    else
361        return NetWMToWinIconThreshold(icon);
362}
363
364/*
365 * Attempt to create a custom icon from the WM_HINTS bitmaps
366 */
367
368static
369 HICON
370winXIconToHICON(Display * pDisplay, Window id, int iconSize)
371{
372    unsigned char *mask, *image = NULL, *imageMask;
373    unsigned char *dst, *src;
374    int planes, bpp, i;
375    unsigned int biggest_size = 0;
376    HDC hDC;
377    ICONINFO ii;
378    XWMHints *hints;
379    HICON hIcon = NULL;
380    uint32_t *biggest_icon = NULL;
381    static Atom _XA_NET_WM_ICON;
382    static int generation;
383    uint32_t *icon, *icon_data = NULL;
384    unsigned long int size;
385    Atom type;
386    int format;
387    unsigned long int left;
388
389    hDC = GetDC(GetDesktopWindow());
390    planes = GetDeviceCaps(hDC, PLANES);
391    bpp = GetDeviceCaps(hDC, BITSPIXEL);
392    ReleaseDC(GetDesktopWindow(), hDC);
393
394    /* Always prefer _NET_WM_ICON icons */
395    if (generation != serverGeneration) {
396        generation = serverGeneration;
397        _XA_NET_WM_ICON = XInternAtom(pDisplay, "_NET_WM_ICON", FALSE);
398    }
399
400    if ((XGetWindowProperty(pDisplay, id, _XA_NET_WM_ICON,
401                            0, MAXINT, FALSE,
402                            AnyPropertyType, &type, &format, &size, &left,
403                            (unsigned char **) &icon_data) == Success) &&
404        (icon_data != NULL)) {
405        for (icon = icon_data; icon < &icon_data[size] && *icon;
406             icon = &icon[icon[0] * icon[1] + 2]) {
407            winDebug("winXIconToHICON: %u x %u NetIcon\n", icon[0], icon[1]);
408
409            /* Icon data size will overflow an int and thus is bigger than the
410               property can possibly be */
411            if ((INT_MAX/icon[0]) < icon[1]) {
412                winDebug("winXIconToHICON: _NET_WM_ICON icon data size overflow\n");
413                break;
414            }
415
416            /* Icon data size is bigger than amount of data remaining */
417            if (&icon[icon[0] * icon[1] + 2] > &icon_data[size]) {
418                winDebug("winXIconToHICON: _NET_WM_ICON data is malformed\n");
419                break;
420            }
421
422            /* Found an exact match to the size we require...  */
423            if (icon[0] == iconSize && icon[1] == iconSize) {
424                winDebug("winXIconToHICON: selected %d x %d NetIcon\n",
425                         iconSize, iconSize);
426                hIcon = NetWMToWinIcon(bpp, icon);
427                break;
428            }
429            /* Otherwise, find the biggest icon and let Windows scale the size */
430            else if (biggest_size < icon[0]) {
431                biggest_icon = icon;
432                biggest_size = icon[0];
433            }
434        }
435
436        if (!hIcon && biggest_icon) {
437            winDebug
438                ("winXIconToHICON: selected %u x %u NetIcon for scaling to %d x %d\n",
439                 biggest_icon[0], biggest_icon[1], iconSize, iconSize);
440
441            hIcon = NetWMToWinIcon(bpp, biggest_icon);
442        }
443
444        XFree(icon_data);
445    }
446
447    if (!hIcon) {
448        winDebug("winXIconToHICON: no suitable NetIcon\n");
449
450        hints = XGetWMHints(pDisplay, id);
451        if (hints) {
452            winDebug("winXIconToHICON: id 0x%x icon_pixmap hint 0x%x\n",
453                     (unsigned int)id,
454                     (unsigned int)hints->icon_pixmap);
455
456            if (hints->icon_pixmap) {
457                Window root;
458                int x, y;
459                unsigned int width, height, border_width, depth;
460                XImage *xImageIcon;
461                XImage *xImageMask = NULL;
462
463                XGetGeometry(pDisplay, hints->icon_pixmap, &root, &x, &y,
464                             &width, &height, &border_width, &depth);
465
466                xImageIcon =
467                    XGetImage(pDisplay, hints->icon_pixmap, 0, 0, width, height,
468                              0xFFFFFFFF, ZPixmap);
469                winDebug("winXIconToHICON: id 0x%x icon Ximage 0x%p\n",
470                         (unsigned int)id, xImageIcon);
471
472                if (hints->icon_mask)
473                    xImageMask =
474                        XGetImage(pDisplay, hints->icon_mask, 0, 0, width,
475                                  height, 0xFFFFFFFF, ZPixmap);
476
477                if (xImageIcon) {
478                    int effBPP, stride, maskStride;
479
480                    /* 15 BPP is really 16BPP as far as we care */
481                    if (bpp == 15)
482                        effBPP = 16;
483                    else
484                        effBPP = bpp;
485
486                    /* Need 16-bit aligned rows for DDBitmaps */
487                    stride = ((iconSize * effBPP + 15) & (~15)) / 8;
488
489                    /* Mask is 1-bit deep */
490                    maskStride = ((iconSize * 1 + 15) & (~15)) / 8;
491
492                    image = malloc(stride * iconSize);
493                    imageMask = malloc(stride * iconSize);
494                    mask = malloc(maskStride * iconSize);
495
496                    /* Default to a completely black mask */
497                    memset(imageMask, 0, stride * iconSize);
498                    memset(mask, 0, maskStride * iconSize);
499
500                    winScaleXImageToWindowsIcon(iconSize, effBPP, stride,
501                                                xImageIcon, image);
502
503                    if (xImageMask) {
504                        winScaleXImageToWindowsIcon(iconSize, 1, maskStride,
505                                                    xImageMask, mask);
506                        winScaleXImageToWindowsIcon(iconSize, effBPP, stride,
507                                                    xImageMask, imageMask);
508                    }
509
510                    /* Now we need to set all bits of the icon which are not masked */
511                    /* on to 0 because Color is really an XOR, not an OR function */
512                    dst = image;
513                    src = imageMask;
514
515                    for (i = 0; i < (stride * iconSize); i++)
516                        if ((*(src++)))
517                            *(dst++) = 0;
518                        else
519                            dst++;
520
521                    ii.fIcon = TRUE;
522                    ii.xHotspot = 0;    /* ignored */
523                    ii.yHotspot = 0;    /* ignored */
524
525                    /* Create Win32 mask from pixmap shape */
526                    ii.hbmMask =
527                        CreateBitmap(iconSize, iconSize, planes, 1, mask);
528
529                    /* Create Win32 bitmap from pixmap */
530                    ii.hbmColor =
531                        CreateBitmap(iconSize, iconSize, planes, bpp, image);
532
533                    /* Merge Win32 mask and bitmap into icon */
534                    hIcon = CreateIconIndirect(&ii);
535
536                    /* Release Win32 mask and bitmap */
537                    DeleteObject(ii.hbmMask);
538                    DeleteObject(ii.hbmColor);
539
540                    /* Free X mask and bitmap */
541                    free(mask);
542                    free(image);
543                    free(imageMask);
544
545                    if (xImageMask)
546                        XDestroyImage(xImageMask);
547
548                    XDestroyImage(xImageIcon);
549                }
550            }
551            XFree(hints);
552        }
553    }
554    return hIcon;
555}
556
557/*
558 * Change the Windows window icon
559 */
560
561#ifdef XWIN_MULTIWINDOW
562void
563winUpdateIcon(HWND hWnd, Display * pDisplay, Window id, HICON hIconNew)
564{
565    HICON hIcon, hIconSmall = NULL, hIconOld;
566
567    /* Start with the icon from preferences, if any */
568    hIcon = hIconNew;
569    hIconSmall = hIconNew;
570
571    /* If we still need an icon, try and get the icon from WM_HINTS */
572    if (!hIcon)
573        hIcon = winXIconToHICON(pDisplay, id, GetSystemMetrics(SM_CXICON));
574    if (!hIconSmall)
575        hIconSmall =
576            winXIconToHICON(pDisplay, id, GetSystemMetrics(SM_CXSMICON));
577
578    /* If we got the small, but not the large one swap them */
579    if (!hIcon && hIconSmall) {
580        hIcon = hIconSmall;
581        hIconSmall = NULL;
582    }
583
584    /* Set the large icon */
585    hIconOld = (HICON) SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
586    /* Delete the old icon if its not the default */
587    winDestroyIcon(hIconOld);
588
589    /* Same for the small icon */
590    hIconOld =
591        (HICON) SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIconSmall);
592    winDestroyIcon(hIconOld);
593}
594
595void
596winInitGlobalIcons(void)
597{
598    int sm_cx = GetSystemMetrics(SM_CXICON);
599    int sm_cxsm = GetSystemMetrics(SM_CXSMICON);
600
601    /* Load default X icon in case it's not ready yet */
602    if (!g_hIconX) {
603        g_hIconX = winOverrideDefaultIcon(sm_cx);
604        g_hSmallIconX = winOverrideDefaultIcon(sm_cxsm);
605    }
606
607    if (!g_hIconX) {
608        g_hIconX = (HICON) LoadImage(g_hInstance,
609                                     MAKEINTRESOURCE(IDI_XWIN),
610                                     IMAGE_ICON,
611                                     GetSystemMetrics(SM_CXICON),
612                                     GetSystemMetrics(SM_CYICON), 0);
613        g_hSmallIconX = (HICON) LoadImage(g_hInstance,
614                                          MAKEINTRESOURCE(IDI_XWIN),
615                                          IMAGE_ICON,
616                                          GetSystemMetrics(SM_CXSMICON),
617                                          GetSystemMetrics(SM_CYSMICON),
618                                          LR_DEFAULTSIZE);
619    }
620}
621
622void
623winSelectIcons(HICON * pIcon, HICON * pSmallIcon)
624{
625    HICON hIcon, hSmallIcon;
626
627    winInitGlobalIcons();
628
629    /* Use default X icon */
630    hIcon = g_hIconX;
631    hSmallIcon = g_hSmallIconX;
632
633    if (pIcon)
634        *pIcon = hIcon;
635
636    if (pSmallIcon)
637        *pSmallIcon = hSmallIcon;
638}
639
640void
641winDestroyIcon(HICON hIcon)
642{
643    /* Delete the icon if its not one of the application defaults or an override */
644    if (hIcon &&
645        hIcon != g_hIconX &&
646        hIcon != g_hSmallIconX && !winIconIsOverride(hIcon))
647        DestroyIcon(hIcon);
648}
649#endif
650