xserver-multidpi/hw/xwin/winmultiwindowicons.c
Jon TURNEY 527cf13135 hw/xwin: Make winOverrideIcon() thread-safe for icon data access
winOverrideIcon() is called from the internal WM client thread.

Accessing server-internal data structures to get icon data or window hints is
not safe, as there is no lock to ensure we do not collide with these data
structures being updated in the server thread.

Rewrite so the internal client thread uses X client calls to obtain this data
safely

We used to also set the icon inside the server when the window was initially
created.  For simplicity, we simply send a message to the internal WM to update
the icon when the window is created (rather than writing different icon update
code which can work in the server thread for that one case...)

extwm mode used to do the icon update in the server. I'm not sure that actually
made much sense.  Let's assume the external WM client can do it instead...

v2
Make sure that WM_WM_ICON_EVENT does nothing for override-redirect windows

v3
Reinstate check that native window actually has expected properties for an X
window before trying to update it's icon; some auxiliary windows owned by the
XWin process don't, which would cause a crash

v4
Various fixes to pixmap icon conversion:
- remove left-over malloc in winScaleXimageToWindowsIcon causing a memory leak
- don't recalculate DDBitmap stride in winScaleXimageToWindowsIcon, when we already have worked it out
- properly check that XGetWindowProperty(NET_WM_ICON) returned some data
- don't try to retrieve WM_HINTS icon_mask if it isn't set
- restore accidentally dropped calculation of effBpp, stride, maskStride of output DDBitmap
- make sure imageMask is zero-initalized before we use it to mask the DDBitmap

v5
Remove a left-over unused variable

v6
Avoid XDestroyImage(NULL) crash if XGetImage failed for icon_pixmap

Signed-off-by: Jon TURNEY <jon.turney@dronecode.org.uk>
Reviewed-by: Colin Harrison <colin.harrison@virgin.net>
2012-08-03 22:47:42 +01:00

631 lines
21 KiB
C

/*
*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 <X11/Xwindows.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "winresource.h"
#include "winprefs.h"
#include "winmsg.h"
#include "winmultiwindowicons.h"
#include "winglobals.h"
/*
* global variables
*/
extern HINSTANCE g_hInstance;
/*
* Scale an X icon ZPixmap into a Windoze icon bitmap
*/
static void
winScaleXImageToWindowsIcon(int iconSize,
int effBPP,
int stride, XImage * 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->bits_per_pixel;
if (pixmap->bits_per_pixel == 15)
effXBPP = 16;
effXDepth = pixmap->depth;
if (pixmap->depth == 15)
effXDepth = 16;
xStride = pixmap->bytes_per_line;
if (stride == 0 || xStride == 0) {
ErrorF("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);
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;
ErrorF("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
*/
static
HICON
winXIconToHICON(Display * pDisplay, Window id, int iconSize)
{
unsigned char *mask, *image = NULL, *imageMask;
unsigned char *dst, *src;
int planes, bpp, i;
int biggest_size = 0;
HDC hDC;
ICONINFO ii;
XWMHints *hints;
HICON hIcon = NULL;
uint32_t *biggest_icon = NULL;
static Atom _XA_NET_WM_ICON;
static int generation;
uint32_t *icon, *icon_data = NULL;
unsigned long int size;
unsigned long int type;
int format;
unsigned long int left;
hDC = GetDC(GetDesktopWindow());
planes = GetDeviceCaps(hDC, PLANES);
bpp = GetDeviceCaps(hDC, BITSPIXEL);
ReleaseDC(GetDesktopWindow(), hDC);
/* Always prefer _NET_WM_ICON icons */
if (generation != serverGeneration) {
generation = serverGeneration;
_XA_NET_WM_ICON = XInternAtom(pDisplay, "_NET_WM_ICON", FALSE);
}
if ((XGetWindowProperty(pDisplay, id, _XA_NET_WM_ICON,
0, MAXINT, FALSE,
AnyPropertyType, &type, &format, &size, &left,
(unsigned char **) &icon_data) == Success) &&
(icon_data != NULL)) {
for (icon = icon_data; icon < &icon_data[size] && *icon;
icon = &icon[icon[0] * icon[1] + 2]) {
/* Find an exact match to the size we require... */
if (icon[0] == iconSize && icon[1] == iconSize) {
winDebug("winXIconToHICON: found %lu x %lu NetIcon\n", icon[0],
icon[1]);
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 %lu x %lu NetIcon for scaling to %u x %u\n",
biggest_icon[0], biggest_icon[1], iconSize, iconSize);
hIcon = NetWMToWinIcon(bpp, biggest_icon);
}
XFree(icon_data);
}
if (!hIcon) {
winDebug("winXIconToHICON: no suitable NetIcon\n");
hints = XGetWMHints(pDisplay, id);
if (hints) {
winDebug("winXIconToHICON: id 0x%x icon_pixmap hint %x\n", id,
hints->icon_pixmap);
if (hints->icon_pixmap) {
Window root;
int x, y;
unsigned int width, height, border_width, depth;
XImage *xImageIcon;
XImage *xImageMask = NULL;
XGetGeometry(pDisplay, hints->icon_pixmap, &root, &x, &y,
&width, &height, &border_width, &depth);
xImageIcon =
XGetImage(pDisplay, hints->icon_pixmap, 0, 0, width, height,
0xFFFFFFFF, ZPixmap);
winDebug("winXIconToHICON: id 0x%x icon Ximage 0x%x\n", id,
xImageIcon);
if (hints->icon_mask)
xImageMask =
XGetImage(pDisplay, hints->icon_mask, 0, 0, width,
height, 0xFFFFFFFF, ZPixmap);
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)
XDestroyImage(xImageMask);
XDestroyImage(xImageIcon);
}
}
XFree(hints);
}
}
return hIcon;
}
/*
* Change the Windows window icon
*/
#ifdef XWIN_MULTIWINDOW
void
winUpdateIcon(HWND hWnd, Display * pDisplay, Window id, HICON hIconNew)
{
HICON hIcon, hIconSmall = NULL, hIconOld;
/* Start with the icon from preferences, if any */
hIcon = hIconNew;
hIconSmall = hIconNew;
/* If we still need an icon, try and get the icon from WM_HINTS */
if (!hIcon)
hIcon = winXIconToHICON(pDisplay, id, GetSystemMetrics(SM_CXICON));
if (!hIconSmall)
hIconSmall =
winXIconToHICON(pDisplay, id, GetSystemMetrics(SM_CXSMICON));
/* If we got the small, but not the large one swap them */
if (!hIcon && hIconSmall) {
hIcon = hIconSmall;
hIconSmall = NULL;
}
/* Set the large icon */
hIconOld = (HICON) SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
/* Delete the old icon if its not the default */
winDestroyIcon(hIconOld);
/* Same for the small icon */
hIconOld =
(HICON) SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIconSmall);
winDestroyIcon(hIconOld);
}
void
winInitGlobalIcons(void)
{
int sm_cx = GetSystemMetrics(SM_CXICON);
int sm_cxsm = GetSystemMetrics(SM_CXSMICON);
/* Load default X icon in case it's not ready yet */
if (!g_hIconX) {
g_hIconX = winOverrideDefaultIcon(sm_cx);
g_hSmallIconX = winOverrideDefaultIcon(sm_cxsm);
}
if (!g_hIconX) {
g_hIconX = (HICON) LoadImage(g_hInstance,
MAKEINTRESOURCE(IDI_XWIN),
IMAGE_ICON,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON), 0);
g_hSmallIconX = (HICON) LoadImage(g_hInstance,
MAKEINTRESOURCE(IDI_XWIN),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTSIZE);
}
}
void
winSelectIcons(HICON * pIcon, HICON * pSmallIcon)
{
HICON hIcon, hSmallIcon;
winInitGlobalIcons();
/* Use default X icon */
hIcon = g_hIconX;
hSmallIcon = g_hSmallIconX;
if (pIcon)
*pIcon = hIcon;
if (pSmallIcon)
*pSmallIcon = hSmallIcon;
}
void
winDestroyIcon(HICON hIcon)
{
/* Delete the icon if its not one of the application defaults or an override */
if (hIcon &&
hIcon != g_hIconX &&
hIcon != g_hSmallIconX && !winIconIsOverride((unsigned long) hIcon))
DestroyIcon(hIcon);
}
#endif