diff options
Diffstat (limited to 'hw/xwin/wmutil/icon_convert.c')
-rw-r--r-- | hw/xwin/wmutil/icon_convert.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/hw/xwin/wmutil/icon_convert.c b/hw/xwin/wmutil/icon_convert.c new file mode 100644 index 000000000..be9f4e027 --- /dev/null +++ b/hw/xwin/wmutil/icon_convert.c @@ -0,0 +1,572 @@ +/* + *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. + * + *Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + *"Software"), to deal in the Software without restriction, including + *without limitation the rights to use, copy, modify, merge, publish, + *distribute, sublicense, and/or sell copies of the Software, and to + *permit persons to whom the Software is furnished to do so, subject to + *the following conditions: + * + *The above copyright notice and this permission notice shall be + *included in all copies or substantial portions of the Software. + * + *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR + *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + *Except as contained in this notice, the name of the XFree86 Project + *shall not be used in advertising or otherwise to promote the sale, use + *or other dealings in this Software without prior written authorization + *from the XFree86 Project. + * + * Authors: Earle F. Philhower, III + */ + +#ifdef HAVE_XWIN_CONFIG_H +#include <xwin-config.h> +#endif + +#ifndef WINVER +#define WINVER 0x0500 +#endif + +#include <xcb/xcb.h> +#include <xcb/xcb_icccm.h> +#include <xcb/xcb_image.h> + +#include <windows.h> + +#include <limits.h> +#include <stdbool.h> + +#include "winmsg.h" + +#include "icon_convert.h" + +extern unsigned long serverGeneration; + +/* + * Scale an X icon ZPixmap into a Windoze icon bitmap + */ + +static void +winScaleXImageToWindowsIcon(int iconSize, + int effBPP, + int stride, xcb_image_t* pixmap, unsigned char *image) +{ + int row, column, effXBPP, effXDepth; + unsigned char *outPtr; + unsigned char *iconData = 0; + int xStride; + float factX, factY; + int posX, posY; + unsigned char *ptr; + unsigned int zero; + unsigned int color; + + effXBPP = pixmap->bpp; + if (pixmap->bpp == 15) + effXBPP = 16; + + effXDepth = pixmap->depth; + if (pixmap->depth == 15) + effXDepth = 16; + + xStride = pixmap->stride; + if (stride == 0 || xStride == 0) { + winError("winScaleXBitmapToWindows - stride or xStride is zero. " + "Bailing.\n"); + return; + } + + /* Get icon data */ + iconData = (unsigned char *) pixmap->data; + + /* Keep aspect ratio */ + factX = ((float) pixmap->width) / ((float) iconSize); + factY = ((float) pixmap->height) / ((float) iconSize); + if (factX > factY) + factY = factX; + else + factX = factY; + + /* Out-of-bounds, fill icon with zero */ + zero = 0; + + for (row = 0; row < iconSize; row++) { + outPtr = image + stride * row; + for (column = 0; column < iconSize; column++) { + posX = factX * column; + posY = factY * row; + + ptr = (unsigned char *) iconData + posY * xStride; + if (effXBPP == 1) { + ptr += posX / 8; + + /* Out of X icon bounds, leave space blank */ + if (posX >= pixmap->width || posY >= pixmap->height) + ptr = (unsigned char *) &zero; + + if ((*ptr) & (1 << (posX & 7))) + switch (effBPP) { + case 32: + *(outPtr++) = 0; + case 24: + *(outPtr++) = 0; + case 16: + *(outPtr++) = 0; + case 8: + *(outPtr++) = 0; + break; + case 1: + outPtr[column / 8] &= ~(1 << (7 - (column & 7))); + break; + } + else + switch (effBPP) { + case 32: + *(outPtr++) = 255; + *(outPtr++) = 255; + *(outPtr++) = 255; + *(outPtr++) = 0; + break; + case 24: + *(outPtr++) = 255; + case 16: + *(outPtr++) = 255; + case 8: + *(outPtr++) = 255; + break; + case 1: + outPtr[column / 8] |= (1 << (7 - (column & 7))); + break; + } + } + else if (effXDepth == 24 || effXDepth == 32) { + ptr += posX * (effXBPP / 8); + + /* Out of X icon bounds, leave space blank */ + if (posX >= pixmap->width || posY >= pixmap->height) + ptr = (unsigned char *) &zero; + color = (((*ptr) << 16) + + ((*(ptr + 1)) << 8) + + ((*(ptr + 2)) << 0)); + switch (effBPP) { + case 32: + *(outPtr++) = *(ptr++); /* b */ + *(outPtr++) = *(ptr++); /* g */ + *(outPtr++) = *(ptr++); /* r */ + *(outPtr++) = (effXDepth == 32) ? *(ptr++) : 0x0; /* alpha */ + break; + case 24: + *(outPtr++) = *(ptr++); + *(outPtr++) = *(ptr++); + *(outPtr++) = *(ptr++); + break; + case 16: + color = ((((*ptr) >> 2) << 10) + + (((*(ptr + 1)) >> 2) << 5) + + (((*(ptr + 2)) >> 2))); + *(outPtr++) = (color >> 8); + *(outPtr++) = (color & 255); + break; + case 8: + color = (((*ptr))) + (((*(ptr + 1)))) + (((*(ptr + 2)))); + color /= 3; + *(outPtr++) = color; + break; + case 1: + if (color) + outPtr[column / 8] |= (1 << (7 - (column & 7))); + else + outPtr[column / 8] &= ~(1 << (7 - (column & 7))); + } + } + else if (effXDepth == 16) { + ptr += posX * (effXBPP / 8); + + /* Out of X icon bounds, leave space blank */ + if (posX >= pixmap->width || posY >= pixmap->height) + ptr = (unsigned char *) &zero; + color = ((*ptr) << 8) + (*(ptr + 1)); + switch (effBPP) { + case 32: + *(outPtr++) = (color & 31) << 2; + *(outPtr++) = ((color >> 5) & 31) << 2; + *(outPtr++) = ((color >> 10) & 31) << 2; + *(outPtr++) = 0; /* resvd */ + break; + case 24: + *(outPtr++) = (color & 31) << 2; + *(outPtr++) = ((color >> 5) & 31) << 2; + *(outPtr++) = ((color >> 10) & 31) << 2; + break; + case 16: + *(outPtr++) = *(ptr++); + *(outPtr++) = *(ptr++); + break; + case 8: + *(outPtr++) = (((color & 31) + + ((color >> 5) & 31) + + ((color >> 10) & 31)) / 3) << 2; + break; + case 1: + if (color) + outPtr[column / 8] |= (1 << (7 - (column & 7))); + else + outPtr[column / 8] &= ~(1 << (7 - (column & 7))); + break; + } /* end switch(effbpp) */ + } /* end if effxbpp==16) */ + } /* end for column */ + } /* end for row */ +} + +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; + 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.fIcon = TRUE; + ii.xHotspot = 0; /* ignored */ + ii.yHotspot = 0; /* ignored */ + ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO *) &bmh, + DIB_RGB_COLORS, (void **) &DIB_pixels, NULL, + 0); + ReleaseDC(NULL, hdc); + + if (!ii.hbmColor) + return NULL; + + 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; + + HDC hdc = GetDC(NULL); + HDC xorDC = CreateCompatibleDC(hdc); + HDC andDC = CreateCompatibleDC(hdc); + + ii.fIcon = TRUE; + ii.xHotspot = 0; /* ignored */ + ii.yHotspot = 0; /* ignored */ + 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; + + winError("OS has icon alpha channel support: %s\n", + hasIconAlphaChannel ? "yes" : "no"); + } + + if (hasIconAlphaChannel && (bpp == 32)) + return NetWMToWinIconAlpha(icon); + else + return NetWMToWinIconThreshold(icon); +} + +/* + * Attempt to create a custom icon from the WM_HINTS bitmaps + */ + +HICON +winXIconToHICON(xcb_connection_t *conn, xcb_window_t id, int iconSize) +{ + unsigned char *mask, *image = NULL, *imageMask; + unsigned char *dst, *src; + int planes, bpp, i; + unsigned int biggest_size = 0; + HDC hDC; + ICONINFO ii; + xcb_icccm_wm_hints_t hints; + HICON hIcon = NULL; + uint32_t *biggest_icon = NULL; + static xcb_atom_t _XA_NET_WM_ICON; + static int generation; + uint32_t *icon, *icon_data = NULL; + unsigned long int size; + + hDC = GetDC(GetDesktopWindow()); + planes = GetDeviceCaps(hDC, PLANES); + bpp = GetDeviceCaps(hDC, BITSPIXEL); + ReleaseDC(GetDesktopWindow(), hDC); + + /* Always prefer _NET_WM_ICON icons */ + if (generation != serverGeneration) { + xcb_intern_atom_reply_t *atom_reply; + xcb_intern_atom_cookie_t atom_cookie; + const char *atomName = "_NET_WM_ICON"; + + generation = serverGeneration; + + _XA_NET_WM_ICON = XCB_NONE; + + atom_cookie = xcb_intern_atom(conn, 0, strlen(atomName), atomName); + atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL); + if (atom_reply) { + _XA_NET_WM_ICON = atom_reply->atom; + free(atom_reply); + } + } + + { + xcb_get_property_cookie_t cookie = xcb_get_property(conn, FALSE, id, _XA_NET_WM_ICON, XCB_ATOM_CARDINAL, 0L, INT_MAX); + xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL); + + if (reply && + ((icon_data = xcb_get_property_value(reply)) != NULL)) { + size = xcb_get_property_value_length(reply)/sizeof(uint32_t); + for (icon = icon_data; icon < &icon_data[size] && *icon; + icon = &icon[icon[0] * icon[1] + 2]) { + winDebug("winXIconToHICON: %u x %u NetIcon\n", icon[0], icon[1]); + + /* Icon data size will overflow an int and thus is bigger than the + property can possibly be */ + if ((INT_MAX/icon[0]) < icon[1]) { + winDebug("winXIconToHICON: _NET_WM_ICON icon data size overflow\n"); + break; + } + + /* Icon data size is bigger than amount of data remaining */ + if (&icon[icon[0] * icon[1] + 2] > &icon_data[size]) { + winDebug("winXIconToHICON: _NET_WM_ICON data is malformed\n"); + break; + } + + /* Found an exact match to the size we require... */ + if (icon[0] == iconSize && icon[1] == iconSize) { + winDebug("winXIconToHICON: selected %d x %d NetIcon\n", + iconSize, iconSize); + hIcon = NetWMToWinIcon(bpp, icon); + break; + } + /* Otherwise, find the biggest icon and let Windows scale the size */ + else if (biggest_size < icon[0]) { + biggest_icon = icon; + biggest_size = icon[0]; + } + } + + if (!hIcon && biggest_icon) { + winDebug + ("winXIconToHICON: selected %u x %u NetIcon for scaling to %d x %d\n", + biggest_icon[0], biggest_icon[1], iconSize, iconSize); + + hIcon = NetWMToWinIcon(bpp, biggest_icon); + } + + free(reply); + } + } + + if (!hIcon) { + xcb_get_property_cookie_t wm_hints_cookie; + + winDebug("winXIconToHICON: no suitable NetIcon\n"); + + wm_hints_cookie = xcb_icccm_get_wm_hints(conn, id); + if (xcb_icccm_get_wm_hints_reply(conn, wm_hints_cookie, &hints, NULL)) { + winDebug("winXIconToHICON: id 0x%x icon_pixmap hint 0x%x\n", + (unsigned int)id, + (unsigned int)hints.icon_pixmap); + + if (hints.icon_pixmap) { + unsigned int width, height; + xcb_image_t *xImageIcon; + xcb_image_t *xImageMask = NULL; + + xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(conn, hints.icon_pixmap); + xcb_get_geometry_reply_t *geom_reply = xcb_get_geometry_reply(conn, geom_cookie, NULL); + + if (geom_reply) { + width = geom_reply->width; + height = geom_reply->height; + + xImageIcon = xcb_image_get(conn, hints.icon_pixmap, + 0, 0, width, height, + 0xFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP); + + winDebug("winXIconToHICON: id 0x%x icon Ximage 0x%p\n", + (unsigned int)id, xImageIcon); + + if (hints.icon_mask) + xImageMask = xcb_image_get(conn, hints.icon_mask, + 0, 0, width, height, + 0xFFFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP); + + if (xImageIcon) { + int effBPP, stride, maskStride; + + /* 15 BPP is really 16BPP as far as we care */ + if (bpp == 15) + effBPP = 16; + else + effBPP = bpp; + + /* Need 16-bit aligned rows for DDBitmaps */ + stride = ((iconSize * effBPP + 15) & (~15)) / 8; + + /* Mask is 1-bit deep */ + maskStride = ((iconSize * 1 + 15) & (~15)) / 8; + + image = malloc(stride * iconSize); + imageMask = malloc(stride * iconSize); + mask = malloc(maskStride * iconSize); + + /* Default to a completely black mask */ + memset(imageMask, 0, stride * iconSize); + memset(mask, 0, maskStride * iconSize); + + winScaleXImageToWindowsIcon(iconSize, effBPP, stride, + xImageIcon, image); + + if (xImageMask) { + winScaleXImageToWindowsIcon(iconSize, 1, maskStride, + xImageMask, mask); + winScaleXImageToWindowsIcon(iconSize, effBPP, stride, + xImageMask, imageMask); + } + + /* Now we need to set all bits of the icon which are not masked */ + /* on to 0 because Color is really an XOR, not an OR function */ + dst = image; + src = imageMask; + + for (i = 0; i < (stride * iconSize); i++) + if ((*(src++))) + *(dst++) = 0; + else + dst++; + + ii.fIcon = TRUE; + ii.xHotspot = 0; /* ignored */ + ii.yHotspot = 0; /* ignored */ + + /* Create Win32 mask from pixmap shape */ + ii.hbmMask = + CreateBitmap(iconSize, iconSize, planes, 1, mask); + + /* Create Win32 bitmap from pixmap */ + ii.hbmColor = + CreateBitmap(iconSize, iconSize, planes, bpp, image); + + /* Merge Win32 mask and bitmap into icon */ + hIcon = CreateIconIndirect(&ii); + + /* Release Win32 mask and bitmap */ + DeleteObject(ii.hbmMask); + DeleteObject(ii.hbmColor); + + /* Free X mask and bitmap */ + free(mask); + free(image); + free(imageMask); + + if (xImageMask) + xcb_image_destroy(xImageMask); + + xcb_image_destroy(xImageIcon); + } + } + } + } + } + return hIcon; +} |