Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xwin / winmultiwindowicons.c
CommitLineData
a09e091a
JB
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 int biggest_size = 0;
376 HDC hDC;
377 ICONINFO ii;
378 XWMHints *hints;
379 HICON hIcon = NULL;
380 uint32_t *biggest_icon = NULL;
381
382 static Atom _XA_NET_WM_ICON;
383 static int generation;
384 uint32_t *icon, *icon_data = NULL;
385 unsigned long int size;
386 unsigned long int type;
387 int format;
388 unsigned long int left;
389
390 hDC = GetDC(GetDesktopWindow());
391 planes = GetDeviceCaps(hDC, PLANES);
392 bpp = GetDeviceCaps(hDC, BITSPIXEL);
393 ReleaseDC(GetDesktopWindow(), hDC);
394
395 /* Always prefer _NET_WM_ICON icons */
396 if (generation != serverGeneration) {
397 generation = serverGeneration;
398 _XA_NET_WM_ICON = XInternAtom(pDisplay, "_NET_WM_ICON", FALSE);
399 }
400
401 if ((XGetWindowProperty(pDisplay, id, _XA_NET_WM_ICON,
402 0, MAXINT, FALSE,
403 AnyPropertyType, &type, &format, &size, &left,
404 (unsigned char **) &icon_data) == Success) &&
405 (icon_data != NULL)) {
406 for (icon = icon_data; icon < &icon_data[size] && *icon;
407 icon = &icon[icon[0] * icon[1] + 2]) {
408 /* Find an exact match to the size we require... */
409 if (icon[0] == iconSize && icon[1] == iconSize) {
410 winDebug("winXIconToHICON: found %lu x %lu NetIcon\n", icon[0],
411 icon[1]);
412 hIcon = NetWMToWinIcon(bpp, icon);
413 break;
414 }
415 /* Otherwise, find the biggest icon and let Windows scale the size */
416 else if (biggest_size < icon[0]) {
417 biggest_icon = icon;
418 biggest_size = icon[0];
419 }
420 }
421
422 if (!hIcon && biggest_icon) {
423 winDebug
424 ("winXIconToHICON: selected %lu x %lu NetIcon for scaling to %u x %u\n",
425 biggest_icon[0], biggest_icon[1], iconSize, iconSize);
426
427 hIcon = NetWMToWinIcon(bpp, biggest_icon);
428 }
429
430 XFree(icon_data);
431 }
432
433 if (!hIcon) {
434 winDebug("winXIconToHICON: no suitable NetIcon\n");
435
436 hints = XGetWMHints(pDisplay, id);
437 if (hints) {
438 winDebug("winXIconToHICON: id 0x%x icon_pixmap hint %x\n", id,
439 hints->icon_pixmap);
440
441 if (hints->icon_pixmap) {
442 Window root;
443 int x, y;
444 unsigned int width, height, border_width, depth;
445 XImage *xImageIcon;
446 XImage *xImageMask = NULL;
447
448 XGetGeometry(pDisplay, hints->icon_pixmap, &root, &x, &y,
449 &width, &height, &border_width, &depth);
450
451 xImageIcon =
452 XGetImage(pDisplay, hints->icon_pixmap, 0, 0, width, height,
453 0xFFFFFFFF, ZPixmap);
454 winDebug("winXIconToHICON: id 0x%x icon Ximage 0x%x\n", id,
455 xImageIcon);
456
457 if (hints->icon_mask)
458 xImageMask =
459 XGetImage(pDisplay, hints->icon_mask, 0, 0, width,
460 height, 0xFFFFFFFF, ZPixmap);
461
462 if (xImageIcon) {
463 int effBPP, stride, maskStride;
464
465 /* 15 BPP is really 16BPP as far as we care */
466 if (bpp == 15)
467 effBPP = 16;
468 else
469 effBPP = bpp;
470
471 /* Need 16-bit aligned rows for DDBitmaps */
472 stride = ((iconSize * effBPP + 15) & (~15)) / 8;
473
474 /* Mask is 1-bit deep */
475 maskStride = ((iconSize * 1 + 15) & (~15)) / 8;
476
477 image = malloc(stride * iconSize);
478 imageMask = malloc(stride * iconSize);
479 mask = malloc(maskStride * iconSize);
480
481 /* Default to a completely black mask */
482 memset(imageMask, 0, stride * iconSize);
483 memset(mask, 0, maskStride * iconSize);
484
485 winScaleXImageToWindowsIcon(iconSize, effBPP, stride,
486 xImageIcon, image);
487
488 if (xImageMask) {
489 winScaleXImageToWindowsIcon(iconSize, 1, maskStride,
490 xImageMask, mask);
491 winScaleXImageToWindowsIcon(iconSize, effBPP, stride,
492 xImageMask, imageMask);
493 }
494
495 /* Now we need to set all bits of the icon which are not masked */
496 /* on to 0 because Color is really an XOR, not an OR function */
497 dst = image;
498 src = imageMask;
499
500 for (i = 0; i < (stride * iconSize); i++)
501 if ((*(src++)))
502 *(dst++) = 0;
503 else
504 dst++;
505
506 ii.fIcon = TRUE;
507 ii.xHotspot = 0; /* ignored */
508 ii.yHotspot = 0; /* ignored */
509
510 /* Create Win32 mask from pixmap shape */
511 ii.hbmMask =
512 CreateBitmap(iconSize, iconSize, planes, 1, mask);
513
514 /* Create Win32 bitmap from pixmap */
515 ii.hbmColor =
516 CreateBitmap(iconSize, iconSize, planes, bpp, image);
517
518 /* Merge Win32 mask and bitmap into icon */
519 hIcon = CreateIconIndirect(&ii);
520
521 /* Release Win32 mask and bitmap */
522 DeleteObject(ii.hbmMask);
523 DeleteObject(ii.hbmColor);
524
525 /* Free X mask and bitmap */
526 free(mask);
527 free(image);
528 free(imageMask);
529
530 if (xImageMask)
531 XDestroyImage(xImageMask);
532
533 XDestroyImage(xImageIcon);
534 }
535 }
536 XFree(hints);
537 }
538 }
539 return hIcon;
540}
541
542/*
543 * Change the Windows window icon
544 */
545
546#ifdef XWIN_MULTIWINDOW
547void
548winUpdateIcon(HWND hWnd, Display * pDisplay, Window id, HICON hIconNew)
549{
550 HICON hIcon, hIconSmall = NULL, hIconOld;
551
552 /* Start with the icon from preferences, if any */
553 hIcon = hIconNew;
554 hIconSmall = hIconNew;
555
556 /* If we still need an icon, try and get the icon from WM_HINTS */
557 if (!hIcon)
558 hIcon = winXIconToHICON(pDisplay, id, GetSystemMetrics(SM_CXICON));
559 if (!hIconSmall)
560 hIconSmall =
561 winXIconToHICON(pDisplay, id, GetSystemMetrics(SM_CXSMICON));
562
563 /* If we got the small, but not the large one swap them */
564 if (!hIcon && hIconSmall) {
565 hIcon = hIconSmall;
566 hIconSmall = NULL;
567 }
568
569 /* Set the large icon */
570 hIconOld = (HICON) SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
571 /* Delete the old icon if its not the default */
572 winDestroyIcon(hIconOld);
573
574 /* Same for the small icon */
575 hIconOld =
576 (HICON) SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIconSmall);
577 winDestroyIcon(hIconOld);
578}
579
580void
581winInitGlobalIcons(void)
582{
583 int sm_cx = GetSystemMetrics(SM_CXICON);
584 int sm_cxsm = GetSystemMetrics(SM_CXSMICON);
585
586 /* Load default X icon in case it's not ready yet */
587 if (!g_hIconX) {
588 g_hIconX = winOverrideDefaultIcon(sm_cx);
589 g_hSmallIconX = winOverrideDefaultIcon(sm_cxsm);
590 }
591
592 if (!g_hIconX) {
593 g_hIconX = (HICON) LoadImage(g_hInstance,
594 MAKEINTRESOURCE(IDI_XWIN),
595 IMAGE_ICON,
596 GetSystemMetrics(SM_CXICON),
597 GetSystemMetrics(SM_CYICON), 0);
598 g_hSmallIconX = (HICON) LoadImage(g_hInstance,
599 MAKEINTRESOURCE(IDI_XWIN),
600 IMAGE_ICON,
601 GetSystemMetrics(SM_CXSMICON),
602 GetSystemMetrics(SM_CYSMICON),
603 LR_DEFAULTSIZE);
604 }
605}
606
607void
608winSelectIcons(HICON * pIcon, HICON * pSmallIcon)
609{
610 HICON hIcon, hSmallIcon;
611
612 winInitGlobalIcons();
613
614 /* Use default X icon */
615 hIcon = g_hIconX;
616 hSmallIcon = g_hSmallIconX;
617
618 if (pIcon)
619 *pIcon = hIcon;
620
621 if (pSmallIcon)
622 *pSmallIcon = hSmallIcon;
623}
624
625void
626winDestroyIcon(HICON hIcon)
627{
628 /* Delete the icon if its not one of the application defaults or an override */
629 if (hIcon &&
630 hIcon != g_hIconX &&
631 hIcon != g_hSmallIconX && !winIconIsOverride(hIcon))
632 DestroyIcon(hIcon);
633}
634#endif