winmultiwindowicons.c revision 706f2543
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#include "win.h"
35#include "dixevents.h"
36#include "winmultiwindowclass.h"
37#include "winprefs.h"
38
39#include "propertyst.h"
40
41#include "propertyst.h"
42#include "windowstr.h"
43
44
45/*
46 * Prototypes for local functions
47 */
48
49static void
50winScaleXBitmapToWindows (int iconSize, int effBPP,
51			  PixmapPtr pixmap, unsigned char *image);
52
53
54/*
55 * Scale an X icon bitmap into a Windoze icon bitmap
56 */
57
58static void
59winScaleXBitmapToWindows (int iconSize,
60			  int effBPP,
61			  PixmapPtr pixmap,
62			  unsigned char *image)
63{
64  int			row, column, effXBPP, effXDepth;
65  unsigned char		*outPtr;
66  char		*iconData = 0;
67  int			stride, xStride;
68  float			factX, factY;
69  int			posX, posY;
70  unsigned char		*ptr;
71  unsigned int		zero;
72  unsigned int		color;
73
74  effXBPP = BitsPerPixel(pixmap->drawable.depth);
75  effXDepth = pixmap->drawable.depth;
76
77  if (pixmap->drawable.bitsPerPixel == 15)
78    effXBPP = 16;
79
80  if (pixmap->drawable.depth == 15)
81    effXDepth = 16;
82
83  /* Need 16-bit aligned rows for DDBitmaps */
84  stride = ((iconSize * effBPP + 15) & (~15)) / 8;
85  xStride = PixmapBytePad (pixmap->drawable.width, pixmap->drawable.depth);
86  if (stride == 0 || xStride == 0)
87    {
88      ErrorF ("winScaleXBitmapToWindows - stride or xStride is zero.  "
89	      "Bailing.\n");
90      return;
91    }
92
93  /* Allocate memory for icon data */
94  iconData = malloc (xStride * pixmap->drawable.height);
95  if (!iconData)
96    {
97      ErrorF ("winScaleXBitmapToWindows - malloc failed for iconData.  "
98	      "Bailing.\n");
99      return;
100    }
101
102  /* Get icon data */
103  miGetImage ((DrawablePtr) &(pixmap->drawable), 0, 0,
104	      pixmap->drawable.width, pixmap->drawable.height,
105	      ZPixmap, 0xffffffff, iconData);
106
107  /* Keep aspect ratio */
108  factX = ((float)pixmap->drawable.width) / ((float)iconSize);
109  factY = ((float)pixmap->drawable.height) / ((float)iconSize);
110  if (factX > factY)
111    factY = factX;
112  else
113    factX = factY;
114
115  /* Out-of-bounds, fill icon with zero */
116  zero = 0;
117
118  for (row = 0; row < iconSize; row++)
119    {
120      outPtr = image + stride * row;
121      for (column = 0; column < iconSize; column++)
122	{
123	  posX = factX * column;
124	  posY = factY * row;
125
126	  ptr = (unsigned char*) iconData + posY*xStride;
127	  if (effXBPP == 1)
128	    {
129	      ptr += posX / 8;
130
131	      /* Out of X icon bounds, leave space blank */
132	      if (posX >= pixmap->drawable.width
133		  || posY >= pixmap->drawable.height)
134		ptr = (unsigned char *) &zero;
135
136	      if ((*ptr) & (1 << (posX & 7)))
137		switch (effBPP)
138		  {
139		  case 32:
140		    *(outPtr++) = 0;
141		  case 24:
142		    *(outPtr++) = 0;
143		  case 16:
144		    *(outPtr++) = 0;
145		  case 8:
146		    *(outPtr++) = 0;
147		    break;
148		  case 1:
149		    outPtr[column / 8] &= ~(1 << (7 - (column & 7)));
150		    break;
151		  }
152	      else
153		switch (effBPP)
154		  {
155		  case 32:
156		    *(outPtr++) = 255;
157		    *(outPtr++) = 255;
158		    *(outPtr++) = 255;
159		    *(outPtr++) = 0;
160		    break;
161		  case 24:
162		    *(outPtr++) = 255;
163		  case 16:
164		    *(outPtr++) = 255;
165		  case 8:
166		    *(outPtr++) = 255;
167		    break;
168		  case 1:
169		    outPtr[column / 8] |= (1 << (7 - (column & 7)));
170		    break;
171		  }
172	    }
173	  else if (effXDepth == 24 || effXDepth == 32)
174	    {
175	      ptr += posX * (effXBPP / 8);
176
177	      /* Out of X icon bounds, leave space blank */
178	      if (posX >= pixmap->drawable.width
179		  || posY >= pixmap->drawable.height)
180		ptr = (unsigned char *) &zero;
181	      color = (((*ptr) << 16)
182		       + ((*(ptr + 1)) << 8)
183		       + ((*(ptr + 2)) << 0));
184	      switch (effBPP)
185		{
186		case 32:
187		  *(outPtr++) = *(ptr++); /* b */
188		  *(outPtr++) = *(ptr++); /* g */
189		  *(outPtr++) = *(ptr++); /* r */
190		  *(outPtr++) = (effXDepth == 32) ? *(ptr++) : 0x0; /* alpha */
191		  break;
192		case 24:
193		  *(outPtr++) = *(ptr++);
194		  *(outPtr++) = *(ptr++);
195		  *(outPtr++) = *(ptr++);
196		  break;
197		case 16:
198		  color = ((((*ptr) >> 2) << 10)
199			   + (((*(ptr + 1)) >> 2) << 5)
200			   + (((*(ptr + 2)) >> 2)));
201		  *(outPtr++) = (color >> 8);
202		  *(outPtr++) = (color & 255);
203		  break;
204		case 8:
205		  color = (((*ptr))) + (((*(ptr + 1)))) + (((*(ptr + 2))));
206		  color /= 3;
207		  *(outPtr++) = color;
208		  break;
209		case 1:
210		  if (color)
211		    outPtr[column / 8] |= (1 << (7 - (column & 7)));
212		  else
213		    outPtr[column / 8] &= ~(1 << (7 - (column & 7)));
214		}
215	    }
216	  else if (effXDepth == 16)
217	    {
218	      ptr += posX * (effXBPP / 8);
219
220	      /* Out of X icon bounds, leave space blank */
221	      if (posX >= pixmap->drawable.width
222		  || posY >= pixmap->drawable.height)
223		ptr = (unsigned char *) &zero;
224	      color = ((*ptr) << 8) + (*(ptr + 1));
225	      switch (effBPP)
226		{
227		case 32:
228		  *(outPtr++) = (color & 31) << 2;
229		  *(outPtr++) = ((color >> 5) & 31) << 2;
230		  *(outPtr++) = ((color >> 10) & 31) << 2;
231		  *(outPtr++) = 0; /* resvd */
232		  break;
233		case 24:
234		  *(outPtr++) = (color & 31) << 2;
235		  *(outPtr++) = ((color >> 5) & 31) << 2;
236		  *(outPtr++) = ((color >> 10) & 31) << 2;
237		  break;
238		case 16:
239		  *(outPtr++) = *(ptr++);
240		  *(outPtr++) = *(ptr++);
241		  break;
242		case 8:
243		  *(outPtr++) = (((color & 31)
244				  + ((color >> 5) & 31)
245				  + ((color >> 10) & 31)) / 3) << 2;
246		  break;
247		case 1:
248		  if (color)
249		    outPtr[column / 8] |= (1 << (7 - (column & 7)));
250		  else
251		    outPtr[column / 8] &= ~(1 << (7 - (column & 7)));
252		  break;
253		} /* end switch(effbpp) */
254	    } /* end if effxbpp==16) */
255	} /* end for column */
256    } /* end for row */
257  free (iconData);
258}
259
260static HICON
261NetWMToWinIconAlpha(uint32_t *icon)
262{
263  int width = icon[0];
264  int height = icon[1];
265  uint32_t *pixels = &icon[2];
266  HICON result;
267  HDC hdc = GetDC(NULL);
268  uint32_t *DIB_pixels;
269  ICONINFO ii = {TRUE};
270  BITMAPV4HEADER bmh = {sizeof(bmh)};
271
272  /* Define an ARGB pixel format used for Color+Alpha icons */
273  bmh.bV4Width = width;
274  bmh.bV4Height = -height; /* Invert the image */
275  bmh.bV4Planes = 1;
276  bmh.bV4BitCount = 32;
277  bmh.bV4V4Compression = BI_BITFIELDS;
278  bmh.bV4AlphaMask = 0xFF000000;
279  bmh.bV4RedMask =   0x00FF0000;
280  bmh.bV4GreenMask = 0x0000FF00;
281  bmh.bV4BlueMask =  0x000000FF;
282
283  ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmh,
284                DIB_RGB_COLORS, (void**)&DIB_pixels, NULL, 0);
285  ReleaseDC(NULL, hdc);
286  ii.hbmMask = CreateBitmap(width, height, 1, 1, NULL);
287  memcpy(DIB_pixels, pixels, height*width*4);
288
289  /* CreateIconIndirect() traditionally required DDBitmaps */
290  /* Systems from WinXP accept 32-bit ARGB DIBitmaps with full 8-bit alpha support */
291  /* The icon is created with a DIB + empty DDB mask (an MS example does the same) */
292  result = CreateIconIndirect(&ii);
293
294  DeleteObject(ii.hbmColor);
295  DeleteObject(ii.hbmMask);
296
297  winDebug("NetWMToWinIconAlpha - %d x %d = %p\n", icon[0], icon[1], result);
298  return result;
299}
300
301static HICON
302NetWMToWinIconThreshold(uint32_t *icon)
303{
304  int width = icon[0];
305  int height = icon[1];
306  uint32_t *pixels = &icon[2];
307  int row, col;
308  HICON result;
309  ICONINFO ii = {TRUE};
310
311  HDC hdc = GetDC(NULL);
312  HDC xorDC = CreateCompatibleDC(hdc);
313  HDC andDC = CreateCompatibleDC(hdc);
314  ii.hbmColor = CreateCompatibleBitmap(hdc, width, height);
315  ii.hbmMask = CreateCompatibleBitmap(hdc, width, height);
316  ReleaseDC(NULL, hdc);
317  SelectObject(xorDC, ii.hbmColor);
318  SelectObject(andDC, ii.hbmMask);
319
320  for (row = 0; row < height; row++) {
321    for (col = 0; col < width; col++) {
322      if ((*pixels & 0xFF000000) > 31<<24) { /* 31 alpha threshold, i.e. opaque above, transparent below */
323	SetPixelV(xorDC, col, row, RGB(((char*)pixels)[2], ((char*)pixels)[1],
324		((char*)pixels)[0]));
325	SetPixelV(andDC, col, row, RGB(0, 0, 0)); /* black mask */
326      }
327      else {
328	SetPixelV(xorDC, col, row, RGB(0, 0, 0));
329	SetPixelV(andDC, col, row, RGB(255, 255, 255)); /* white mask */
330      }
331      pixels++;
332    }
333  }
334  DeleteDC(xorDC);
335  DeleteDC(andDC);
336
337  result = CreateIconIndirect(&ii);
338
339  DeleteObject(ii.hbmColor);
340  DeleteObject(ii.hbmMask );
341
342  winDebug("NetWMToWinIconThreshold - %d x %d = %p\n", icon[0], icon[1], result);
343  return result;
344}
345
346static HICON
347NetWMToWinIcon(int bpp, uint32_t *icon)
348{
349  static Bool hasIconAlphaChannel = FALSE;
350  static BOOL versionChecked = FALSE;
351
352  if (!versionChecked)
353    {
354      OSVERSIONINFOEX osvi = {0};
355      ULONGLONG dwlConditionMask = 0;
356
357      osvi.dwOSVersionInfoSize = sizeof (osvi);
358      osvi.dwMajorVersion = 5;
359      osvi.dwMinorVersion = 1;
360
361      /* Windows versions later than XP have icon alpha channel suport, 2000 does not */
362      VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
363      VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
364      hasIconAlphaChannel = VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask);
365      versionChecked = TRUE;
366
367      ErrorF("OS has icon alpha channel support: %s\n", hasIconAlphaChannel ? "yes" : "no");
368    }
369
370  if (hasIconAlphaChannel && (bpp==32))
371    return NetWMToWinIconAlpha(icon);
372  else
373    return NetWMToWinIconThreshold(icon);
374}
375
376static pointer
377GetWindowProp(WindowPtr pWin, Atom name, long int *size_return)
378{
379  struct _Window	*pwin;
380  struct _Property	*prop;
381
382  if (!pWin || !name) {
383    ErrorF ("GetWindowProp - pWin or name was NULL\n");
384    return 0;
385  }
386  pwin = (struct _Window*) pWin;
387  if (!pwin->optional) return NULL;
388  for (prop = (struct _Property *) pwin->optional->userProps;
389       prop;
390       prop=prop->next){
391    if (prop->propertyName == name) {
392      *size_return=prop->size;
393      return prop->data;
394    }
395  }
396  return NULL;
397}
398
399/*
400 * Attempt to create a custom icon from the WM_HINTS bitmaps
401 */
402
403HICON
404winXIconToHICON (WindowPtr pWin, int iconSize)
405{
406  unsigned char		*mask, *image, *imageMask;
407  unsigned char		*dst, *src;
408  PixmapPtr		iconPtr;
409  PixmapPtr		maskPtr;
410  int			planes, bpp, effBPP, stride, maskStride, i;
411  int			biggest_size = 0;
412  HDC			hDC;
413  ICONINFO		ii;
414  WinXWMHints		hints;
415  HICON			hIcon = NULL;
416  uint32_t		*biggest_icon = NULL;
417
418  /* Try to get _NET_WM_ICON icons first */
419  static Atom _XA_NET_WM_ICON;
420  static int generation;
421  uint32_t *icon, *icon_data = NULL;
422  long int size=0;
423
424  hDC = GetDC (GetDesktopWindow ());
425  planes = GetDeviceCaps (hDC, PLANES);
426  bpp = GetDeviceCaps (hDC, BITSPIXEL);
427  ReleaseDC (GetDesktopWindow (), hDC);
428
429  if (generation != serverGeneration) {
430     generation = serverGeneration;
431     _XA_NET_WM_ICON = MakeAtom("_NET_WM_ICON", 12, TRUE);
432  }
433
434  if (_XA_NET_WM_ICON) icon_data = GetWindowProp(pWin, _XA_NET_WM_ICON, &size);
435  if (icon_data)
436    {
437      for(icon = icon_data;
438	  icon < &icon_data[size] && *icon;
439	  icon = &icon[icon[0]*icon[1]+2])
440	{
441	  if (icon[0]==iconSize && icon[1]==iconSize)
442            return NetWMToWinIcon(bpp, icon);
443	  /* Find the biggest icon and let Windows scale the size */
444	  else if (biggest_size < icon[0])
445	    {
446	      biggest_icon = icon;
447	      biggest_size = icon[0];
448	    }
449	}
450      if (biggest_icon)
451	return NetWMToWinIcon(bpp, biggest_icon);
452    }
453  winDebug("winXIconToHICON - pWin %x: no suitable NetIcon\n",(int)pWin, iconSize);
454
455  winMultiWindowGetWMHints (pWin, &hints);
456  if (!hints.icon_pixmap) return NULL;
457
458  dixLookupResourceByType((pointer) &iconPtr, hints.icon_pixmap, RT_PIXMAP,
459				NullClient, DixUnknownAccess);
460
461  if (!iconPtr) return NULL;
462
463  /* 15 BPP is really 16BPP as far as we care */
464  if (bpp == 15)
465    effBPP = 16;
466  else
467    effBPP = bpp;
468
469  /* Need 16-bit aligned rows for DDBitmaps */
470  stride = ((iconSize * effBPP + 15) & (~15)) / 8;
471
472  /* Mask is 1-bit deep */
473  maskStride = ((iconSize * 1 + 15) & (~15)) / 8;
474
475  image = malloc (stride * iconSize);
476  imageMask = malloc (stride * iconSize);
477  /* Default to a completely black mask */
478  mask = calloc (maskStride, iconSize);
479
480  winScaleXBitmapToWindows (iconSize, effBPP, iconPtr, image);
481  dixLookupResourceByType((pointer) &maskPtr, hints.icon_mask, RT_PIXMAP,
482				NullClient, DixUnknownAccess);
483
484  if (maskPtr)
485    {
486      winScaleXBitmapToWindows (iconSize, 1, maskPtr, mask);
487
488      winScaleXBitmapToWindows (iconSize, effBPP, maskPtr, imageMask);
489
490      /* Now we need to set all bits of the icon which are not masked */
491      /* on to 0 because Color is really an XOR, not an OR function */
492      dst = image;
493      src = imageMask;
494
495      for (i = 0; i < (stride * iconSize); i++)
496	if ((*(src++)))
497	  *(dst++) = 0;
498	else
499	  dst++;
500    }
501
502  ii.fIcon = TRUE;
503  ii.xHotspot = 0; /* ignored */
504  ii.yHotspot = 0; /* ignored */
505
506  /* Create Win32 mask from pixmap shape */
507  ii.hbmMask = CreateBitmap (iconSize, iconSize, planes, 1, mask);
508
509  /* Create Win32 bitmap from pixmap */
510  ii.hbmColor = CreateBitmap (iconSize, iconSize, planes, bpp, image);
511
512  /* Merge Win32 mask and bitmap into icon */
513  hIcon = CreateIconIndirect (&ii);
514
515  /* Release Win32 mask and bitmap */
516  DeleteObject (ii.hbmMask);
517  DeleteObject (ii.hbmColor);
518
519  /* Free X mask and bitmap */
520  free (mask);
521  free (image);
522  free (imageMask);
523
524  return hIcon;
525}
526
527
528
529/*
530 * Change the Windows window icon
531 */
532
533#ifdef XWIN_MULTIWINDOW
534void
535winUpdateIcon (Window id)
536{
537  WindowPtr		pWin;
538  HICON			hIcon, hIconSmall=NULL, hIconOld;
539
540  dixLookupResourceByType((pointer) &pWin, id, RT_WINDOW, NullClient, DixUnknownAccess);
541  if (pWin)
542    {
543      winWindowPriv(pWin);
544      if (pWinPriv->hWnd) {
545        hIcon = winOverrideIcon ((unsigned long)pWin);
546        if (!hIcon) {
547          hIcon = winXIconToHICON (pWin, GetSystemMetrics(SM_CXICON));
548          if (!hIcon) {
549            hIcon = g_hIconX;
550            hIconSmall = g_hSmallIconX;
551          } else {
552            /* Leave undefined if not found */
553            hIconSmall = winXIconToHICON (pWin, GetSystemMetrics(SM_CXSMICON));
554          }
555        }
556
557        /* Set the large icon */
558        hIconOld = (HICON) SendMessage (pWinPriv->hWnd,
559                                        WM_SETICON, ICON_BIG, (LPARAM) hIcon);
560
561        /* Delete the icon if its not the default */
562        winDestroyIcon(hIconOld);
563
564        /* Same for the small icon */
565        hIconOld = (HICON) SendMessage (pWinPriv->hWnd,
566                                        WM_SETICON, ICON_SMALL, (LPARAM) hIconSmall);
567        winDestroyIcon(hIconOld);
568      }
569  }
570}
571
572void winInitGlobalIcons (void)
573{
574  int sm_cx = GetSystemMetrics(SM_CXICON);
575  int sm_cxsm = GetSystemMetrics(SM_CXSMICON);
576  /* Load default X icon in case it's not ready yet */
577  if (!g_hIconX)
578    {
579      g_hIconX = winOverrideDefaultIcon(sm_cx);
580      g_hSmallIconX = winOverrideDefaultIcon(sm_cxsm);
581    }
582
583  if (!g_hIconX)
584    {
585      g_hIconX = (HICON)LoadImage (g_hInstance,
586	      MAKEINTRESOURCE(IDI_XWIN),
587	      IMAGE_ICON,
588	      GetSystemMetrics(SM_CXICON),
589	      GetSystemMetrics(SM_CYICON),
590	      0);
591      g_hSmallIconX = (HICON)LoadImage (g_hInstance,
592	      MAKEINTRESOURCE(IDI_XWIN),
593	      IMAGE_ICON,
594	      GetSystemMetrics(SM_CXSMICON),
595	      GetSystemMetrics(SM_CYSMICON),
596	      LR_DEFAULTSIZE);
597    }
598}
599
600void winSelectIcons(WindowPtr pWin, HICON *pIcon, HICON *pSmallIcon)
601{
602  HICON hIcon, hSmallIcon;
603
604  winInitGlobalIcons();
605
606  /* Try and get the icon from WM_HINTS */
607  hIcon = winXIconToHICON (pWin, GetSystemMetrics(SM_CXICON));
608  hSmallIcon = winXIconToHICON (pWin, GetSystemMetrics(SM_CXSMICON));
609
610  /* If we got the small, but not the large one swap them */
611  if (!hIcon && hSmallIcon)
612  {
613      hIcon = hSmallIcon;
614      hSmallIcon = NULL;
615  }
616
617  /* Use default X icon if no icon loaded from WM_HINTS */
618  if (!hIcon) {
619    hIcon = g_hIconX;
620    hSmallIcon = g_hSmallIconX;
621  }
622
623  if (pIcon)
624    *pIcon = hIcon;
625  else
626    winDestroyIcon(hIcon);
627  if (pSmallIcon)
628    *pSmallIcon = hSmallIcon;
629  else
630    winDestroyIcon(hSmallIcon);
631}
632
633void winDestroyIcon(HICON hIcon)
634{
635  /* Delete the icon if its not the default */
636  if (hIcon &&
637      hIcon != g_hIconX &&
638      hIcon != g_hSmallIconX &&
639      !winIconIsOverride((unsigned long)hIcon))
640    DestroyIcon (hIcon);
641}
642#endif
643