From 63f40559980f8cac0465aff39c52e5e764bde5b5 Mon Sep 17 00:00:00 2001 From: Colin Harrison Date: Tue, 28 Jul 2009 20:16:30 +0100 Subject: Xming: Add NET_WM_ICON to native icon conversion fd.o bugzilla #4491 originally from a patch by Joe Krahn Updated NET_WM_ICON converters from Xming 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 on a threshod value Fix warning in winScaleXBitmapToWindows() about signedness of *iconData Plus misc cleanup in winUpdateIcon() - only check for NULL hWnd once - don't delete default icons Signed-off-by: Jon TURNEY --- hw/xwin/winms.h | 1 + hw/xwin/winmultiwindowicons.c | 240 +++++++++++++++++++++++++++++++++++------ 2 files changed, 208 insertions(+), 33 deletions(-) --- origsrc/xorg-server-1.6.2/hw/xwin/winms.h 2008-10-13 00:19:00.000000000 +0100 +++ src/xorg-server-1.6.2/hw/xwin/winms.h 2009-07-28 20:49:20.203125000 +0100 @@ -35,6 +35,7 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#define WINVER 0x0500 #include #include --- origsrc/xorg-server-1.6.2/hw/xwin/winmultiwindowicons.c 2009-07-28 20:48:39.328125000 +0100 +++ src/xorg-server-1.6.2/hw/xwin/winmultiwindowicons.c 2009-07-28 20:49:20.203125000 +0100 @@ -36,6 +36,7 @@ #include "winmultiwindowclass.h" #include "winprefs.h" +#include "propertyst.h" /* * External global variables @@ -66,7 +67,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; @@ -126,7 +127,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,7 +191,7 @@ winScaleXBitmapToWindows (int iconSize, *(outPtr++) = *(ptr++); // b *(outPtr++) = *(ptr++); // g *(outPtr++) = *(ptr++); // r - *(outPtr++) = 0; // resvd + *(outPtr++) = (effXDepth == 32) ? *(ptr++) : 0x0; // alpha break; case 24: *(outPtr++) = *(ptr++); @@ -260,6 +261,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("NetWMToWinIcon - %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 @@ -273,23 +412,57 @@ winXIconToHICON (WindowPtr pWin, int ico 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; - winMultiWindowGetWMHints (pWin, &hints); - if (!hints.icon_pixmap) return 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; - 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); + + 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; + + iconPtr = (PixmapPtr) LookupIDByType (hints.icon_pixmap, RT_PIXMAP); + if (!iconPtr) return NULL; + /* 15 BPP is really 16BPP as far as we care */ if (bpp == 15) effBPP = 16; @@ -364,10 +537,14 @@ void winUpdateIcon (Window id) { WindowPtr pWin; - HICON hIcon, hiconOld; + HICON hIcon, hIconSmall, hiconOld; pWin = (WindowPtr) LookupIDByType (id, RT_WINDOW); if (!pWin) return; + + winWindowPriv(pWin); + if (!pWinPriv->hWnd) return; + hIcon = (HICON)winOverrideIcon ((unsigned long)pWin); if (!hIcon) @@ -375,31 +552,28 @@ winUpdateIcon (Window id) if (hIcon) { - winWindowPriv(pWin); - - if (pWinPriv->hWnd) - { - hiconOld = (HICON) SetClassLong (pWinPriv->hWnd, - GCL_HICON, - (int) hIcon); + hiconOld = (HICON) SetClassLong (pWinPriv->hWnd, + GCL_HICON, + (int) hIcon); - /* Delete the icon if its not the default */ - winDestroyIcon(hiconOld); - } + /* Delete the icon if its not the default */ + if (hiconOld != g_hIconX) + winDestroyIcon(hiconOld); } - - hIcon = winXIconToHICON (pWin, GetSystemMetrics(SM_CXSMICON)); - if (hIcon) - { - winWindowPriv(pWin); - if (pWinPriv->hWnd) - { - hiconOld = (HICON) SetClassLong (pWinPriv->hWnd, - GCL_HICONSM, - (int) hIcon); - winDestroyIcon (hiconOld); - } + hIconSmall = (HICON)winOverrideIcon ((unsigned long)pWin); + + if (!hIconSmall) + hIconSmall = winXIconToHICON (pWin, GetSystemMetrics(SM_CXSMICON)); + + if (hIconSmall) + { + hiconOld = (HICON) SetClassLong (pWinPriv->hWnd, + GCL_HICONSM, + (int) hIconSmall); + if (hiconOld != g_hSmallIconX) + winDestroyIcon (hiconOld); + } }