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