From a400dbb38f93030d51afe806b4b20d5ef501c855 Mon Sep 17 00:00:00 2001 From: Colin Harrison Date: Wed, 12 Aug 2009 17:48:36 +0100 Subject: [PATCH] Xming: Add NET_WM_ICON to native icon conversion fd.o bugzilla #4491 originally from a patch by Joe Krahn Convert a NET_WM_ICON to a native icon by converting to a native bitmap and then using CreateIconIndirect() Don't use icon alpha on Windows 2000 or if display isn't 32-bit, convert alpha channel to a 1-bit transparency mask using a threshold value Fix warning in winScaleXBitmapToWindows() about signedness of *iconData Signed-off-by: Jon TURNEY --- hw/xwin/winms.h | 3 + hw/xwin/winmultiwindowicons.c | 201 +++++++++++++++++++++++++++++++--- 2 files changed, 190 insertions(+), 14 deletions(-) diff --git a/hw/xwin/winms.h b/hw/xwin/winms.h index 1ad30dc0b..60f7eb906 100644 --- a/hw/xwin/winms.h +++ b/hw/xwin/winms.h @@ -35,6 +35,9 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#ifndef WINVER +#define WINVER 0x0500 +#endif #include #include diff --git a/hw/xwin/winmultiwindowicons.c b/hw/xwin/winmultiwindowicons.c index a23f9624a..e16b2a3c8 100644 --- a/hw/xwin/winmultiwindowicons.c +++ b/hw/xwin/winmultiwindowicons.c @@ -36,6 +36,7 @@ #include "winmultiwindowclass.h" #include "winprefs.h" +#include "propertyst.h" #include "propertyst.h" #include "windowstr.h" @@ -69,7 +70,7 @@ winScaleXBitmapToWindows (int iconSize, { int row, column, effXBPP, effXDepth; unsigned char *outPtr; - unsigned char *iconData = 0; + char *iconData = 0; int stride, xStride; float factX, factY; int posX, posY; @@ -129,7 +130,7 @@ winScaleXBitmapToWindows (int iconSize, posX = factX * column; posY = factY * row; - ptr = iconData + posY*xStride; + ptr = (unsigned char*) iconData + posY*xStride; if (effXBPP == 1) { ptr += posX / 8; @@ -190,10 +191,10 @@ winScaleXBitmapToWindows (int iconSize, switch (effBPP) { case 32: - *(outPtr++) = *(ptr++); // b - *(outPtr++) = *(ptr++); // g - *(outPtr++) = *(ptr++); // r - *(outPtr++) = 0; // resvd + *(outPtr++) = *(ptr++); /* b */ + *(outPtr++) = *(ptr++); /* g */ + *(outPtr++) = *(ptr++); /* r */ + *(outPtr++) = (effXDepth == 32) ? *(ptr++) : 0x0; /* alpha */ break; case 24: *(outPtr++) = *(ptr++); @@ -234,7 +235,7 @@ winScaleXBitmapToWindows (int iconSize, *(outPtr++) = (color & 31) << 2; *(outPtr++) = ((color >> 5) & 31) << 2; *(outPtr++) = ((color >> 10) & 31) << 2; - *(outPtr++) = 0; // resvd + *(outPtr++) = 0; /* resvd */ break; case 24: *(outPtr++) = (color & 31) << 2; @@ -263,6 +264,144 @@ winScaleXBitmapToWindows (int iconSize, free (iconData); } +static HICON +NetWMToWinIconAlpha(uint32_t *icon) +{ + int width = icon[0]; + int height = icon[1]; + uint32_t *pixels = &icon[2]; + HICON result; + HDC hdc = GetDC(NULL); + uint32_t *DIB_pixels; + ICONINFO ii = {TRUE}; + BITMAPV4HEADER bmh = {sizeof(bmh)}; + + /* Define an ARGB pixel format used for Color+Alpha icons */ + bmh.bV4Width = width; + bmh.bV4Height = -height; /* Invert the image */ + bmh.bV4Planes = 1; + bmh.bV4BitCount = 32; + bmh.bV4V4Compression = BI_BITFIELDS; + bmh.bV4AlphaMask = 0xFF000000; + bmh.bV4RedMask = 0x00FF0000; + bmh.bV4GreenMask = 0x0000FF00; + bmh.bV4BlueMask = 0x000000FF; + + ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmh, + DIB_RGB_COLORS, (void**)&DIB_pixels, NULL, 0); + ReleaseDC(NULL, hdc); + ii.hbmMask = CreateBitmap(width, height, 1, 1, NULL); + memcpy(DIB_pixels, pixels, height*width*4); + + /* CreateIconIndirect() traditionally required DDBitmaps */ + /* Systems from WinXP accept 32-bit ARGB DIBitmaps with full 8-bit alpha support */ + /* The icon is created with a DIB + empty DDB mask (an MS example does the same) */ + result = CreateIconIndirect(&ii); + + DeleteObject(ii.hbmColor); + DeleteObject(ii.hbmMask); + + winDebug("NetWMToWinIconAlpha - %d x %d = %p\n", icon[0], icon[1], result); + return result; +} + +static HICON +NetWMToWinIconThreshold(uint32_t *icon) +{ + int width = icon[0]; + int height = icon[1]; + uint32_t *pixels = &icon[2]; + int row, col; + HICON result; + ICONINFO ii = {TRUE}; + + HDC hdc = GetDC(NULL); + HDC xorDC = CreateCompatibleDC(hdc); + HDC andDC = CreateCompatibleDC(hdc); + ii.hbmColor = CreateCompatibleBitmap(hdc, width, height); + ii.hbmMask = CreateCompatibleBitmap(hdc, width, height); + ReleaseDC(NULL, hdc); + SelectObject(xorDC, ii.hbmColor); + SelectObject(andDC, ii.hbmMask); + + for (row = 0; row < height; row++) { + for (col = 0; col < width; col++) { + if ((*pixels & 0xFF000000) > 31<<24) { /* 31 alpha threshold, i.e. opaque above, transparent below */ + SetPixelV(xorDC, col, row, RGB(((char*)pixels)[2], ((char*)pixels)[1], + ((char*)pixels)[0])); + SetPixelV(andDC, col, row, RGB(0, 0, 0)); /* black mask */ + } + else { + SetPixelV(xorDC, col, row, RGB(0, 0, 0)); + SetPixelV(andDC, col, row, RGB(255, 255, 255)); /* white mask */ + } + pixels++; + } + } + DeleteDC(xorDC); + DeleteDC(andDC); + + result = CreateIconIndirect(&ii); + + DeleteObject(ii.hbmColor); + DeleteObject(ii.hbmMask ); + + winDebug("NetWMToWinIconThreshold - %d x %d = %p\n", icon[0], icon[1], result); + return result; +} + +static HICON +NetWMToWinIcon(int bpp, uint32_t *icon) +{ + static Bool hasIconAlphaChannel = FALSE; + static BOOL versionChecked = FALSE; + + if (!versionChecked) + { + OSVERSIONINFOEX osvi = {0}; + ULONGLONG dwlConditionMask = 0; + + osvi.dwOSVersionInfoSize = sizeof (osvi); + osvi.dwMajorVersion = 5; + osvi.dwMinorVersion = 1; + + /* Windows versions later than XP have icon alpha channel suport, 2000 does not */ + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + hasIconAlphaChannel = VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask); + versionChecked = TRUE; + + ErrorF("OS has icon alpha channel support: %s\n", hasIconAlphaChannel ? "yes" : "no"); + } + + if (hasIconAlphaChannel && (bpp==32)) + return NetWMToWinIconAlpha(icon); + else + return NetWMToWinIconThreshold(icon); +} + +static pointer +GetWindowProp(WindowPtr pWin, Atom name, long int *size_return) +{ + struct _Window *pwin; + struct _Property *prop; + + if (!pWin || !name) { + ErrorF ("GetWindowProp - pWin or name was NULL\n"); + return 0; + } + pwin = (struct _Window*) pWin; + if (!pwin->optional) return NULL; + for (prop = (struct _Property *) pwin->optional->userProps; + prop; + prop=prop->next){ + if (prop->propertyName == name) { + *size_return=prop->size; + return prop->data; + } + } + return NULL; +} /* * Attempt to create a custom icon from the WM_HINTS bitmaps @@ -276,10 +415,49 @@ winXIconToHICON (WindowPtr pWin, int iconSize) PixmapPtr iconPtr; PixmapPtr maskPtr; int planes, bpp, effBPP, stride, maskStride, i; + int biggest_size = 0; HDC hDC; ICONINFO ii; WinXWMHints hints; - HICON hIcon; + HICON hIcon = NULL; + uint32_t *biggest_icon = NULL; + + /* Try to get _NET_WM_ICON icons first */ + static Atom _XA_NET_WM_ICON; + static int generation; + uint32_t *icon, *icon_data = NULL; + long int size=0; + + hDC = GetDC (GetDesktopWindow ()); + planes = GetDeviceCaps (hDC, PLANES); + bpp = GetDeviceCaps (hDC, BITSPIXEL); + ReleaseDC (GetDesktopWindow (), hDC); + + if (generation != serverGeneration) { + generation = serverGeneration; + _XA_NET_WM_ICON = MakeAtom("_NET_WM_ICON", 12, TRUE); + } + + if (_XA_NET_WM_ICON) icon_data = GetWindowProp(pWin, _XA_NET_WM_ICON, &size); + if (icon_data) + { + for(icon = icon_data; + icon < &icon_data[size] && *icon; + icon = &icon[icon[0]*icon[1]+2]) + { + if (icon[0]==iconSize && icon[1]==iconSize) + return NetWMToWinIcon(bpp, icon); + /* Find the biggest icon and let Windows scale the size */ + else if (biggest_size < icon[0]) + { + biggest_icon = icon; + biggest_size = icon[0]; + } + } + if (biggest_icon) + return NetWMToWinIcon(bpp, biggest_icon); + } + winDebug("winXIconToHICON - pWin %x: no suitable NetIcon\n",(int)pWin, iconSize); winMultiWindowGetWMHints (pWin, &hints); if (!hints.icon_pixmap) return NULL; @@ -287,12 +465,7 @@ winXIconToHICON (WindowPtr pWin, int iconSize) iconPtr = (PixmapPtr) LookupIDByType (hints.icon_pixmap, RT_PIXMAP); if (!iconPtr) return NULL; - - hDC = GetDC (GetDesktopWindow ()); - planes = GetDeviceCaps (hDC, PLANES); - bpp = GetDeviceCaps (hDC, BITSPIXEL); - ReleaseDC (GetDesktopWindow (), hDC); - + /* 15 BPP is really 16BPP as far as we care */ if (bpp == 15) effBPP = 16;