1 /*
   2  * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.awt.X11;
  26 
  27 import java.awt.*;
  28 import java.awt.image.*;
  29 import sun.awt.X11GraphicsConfig;
  30 import sun.awt.image.ToolkitImage;
  31 import sun.awt.image.ImageRepresentation;
  32 
  33 import sun.util.logging.PlatformLogger;
  34 
  35 public class XIconWindow extends XBaseWindow {
  36     private final static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XIconWindow");
  37     XDecoratedPeer parent;
  38     Dimension size;
  39     long iconPixmap = 0;
  40     long iconMask = 0;
  41     int iconWidth = 0;
  42     int iconHeight = 0;
  43     XIconWindow(XDecoratedPeer parent) {
  44         super(new XCreateWindowParams(new Object[] {
  45             PARENT, parent,
  46             DELAYED, Boolean.TRUE}));
  47     }
  48 
  49     void instantPreInit(XCreateWindowParams params) {
  50         super.instantPreInit(params);
  51         this.parent = (XDecoratedPeer)params.get(PARENT);
  52     }
  53 
  54     /**
  55      * @return array of XIconsSize structures, caller must free this array after use.
  56      */
  57     private XIconSize[] getIconSizes() {
  58         XToolkit.awtLock();
  59         try {
  60             AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData();
  61             final long screen = adata.get_awt_visInfo().get_screen();
  62             final long display = XToolkit.getDisplay();
  63 
  64             if (log.isLoggable(PlatformLogger.FINEST)) {
  65                 log.finest(adata.toString());
  66             }
  67 
  68             long status =
  69                 XlibWrapper.XGetIconSizes(display, XToolkit.getDefaultRootWindow(),
  70                                           XlibWrapper.larg1, XlibWrapper.iarg1);
  71             if (status == 0) {
  72                 return null;
  73             }
  74             int count = Native.getInt(XlibWrapper.iarg1);
  75             long sizes_ptr = Native.getLong(XlibWrapper.larg1); // XIconSize*
  76             log.finest("count = {1}, sizes_ptr = {0}", Long.valueOf(sizes_ptr), Integer.valueOf(count));
  77             XIconSize[] res = new XIconSize[count];
  78             for (int i = 0; i < count; i++, sizes_ptr += XIconSize.getSize()) {
  79                 res[i] = new XIconSize(sizes_ptr);
  80                 log.finest("sizes_ptr[{1}] = {0}", res[i], Integer.valueOf(i));
  81             }
  82             return res;
  83         } finally {
  84             XToolkit.awtUnlock();
  85         }
  86     }
  87 
  88     private Dimension calcIconSize(int widthHint, int heightHint) {
  89         if (XWM.getWMID() == XWM.ICE_WM) {
  90             // ICE_WM has a bug - it only displays icons of the size
  91             // 16x16, while reporting 32x32 in its size list
  92             log.finest("Returning ICE_WM icon size: 16x16");
  93             return new Dimension(16, 16);
  94         }
  95 
  96         XIconSize[] sizeList = getIconSizes();
  97         if (log.isLoggable(PlatformLogger.FINEST)) {
  98             log.finest("Icon sizes: {0}", (Object[]) sizeList);
  99         }
 100         if (sizeList == null) {
 101             // No icon sizes so we simply fall back to 16x16
 102             return new Dimension(16, 16);
 103         }
 104         boolean found = false;
 105         int dist = 0xffffffff, newDist, diff = 0, closestHeight, closestWidth;
 106         int saveWidth = 0, saveHeight = 0;
 107         for (int i = 0; i < sizeList.length; i++) {
 108             if (widthHint >= sizeList[i].get_min_width() &&
 109                 widthHint <= sizeList[i].get_max_width() &&
 110                 heightHint >= sizeList[i].get_min_height() &&
 111                 heightHint <= sizeList[i].get_max_height()) {
 112                 found = true;
 113                 if ((((widthHint-sizeList[i].get_min_width())
 114                       % sizeList[i].get_width_inc()) == 0) &&
 115                     (((heightHint-sizeList[i].get_min_height())
 116                       % sizeList[i].get_height_inc()) ==0)) {
 117                     /* Found an exact match */
 118                     saveWidth = widthHint;
 119                     saveHeight = heightHint;
 120                     dist = 0;
 121                     break;
 122                 }
 123                 diff = widthHint - sizeList[i].get_min_width();
 124                 if (diff == 0) {
 125                     closestWidth = widthHint;
 126                 } else {
 127                     diff = diff%sizeList[i].get_width_inc();
 128                     closestWidth = widthHint - diff;
 129                 }
 130                 diff = heightHint - sizeList[i].get_min_height();
 131                 if (diff == 0) {
 132                     closestHeight = heightHint;
 133                 } else {
 134                     diff = diff%sizeList[i].get_height_inc();
 135                     closestHeight = heightHint - diff;
 136                 }
 137                 newDist = closestWidth*closestWidth +
 138                     closestHeight*closestHeight;
 139                 if (dist > newDist) {
 140                     saveWidth = closestWidth;
 141                     saveHeight = closestHeight;
 142                     dist = newDist;
 143                 }
 144             }
 145         }
 146         if (log.isLoggable(PlatformLogger.FINEST)) {
 147             log.finest("found=" + found);
 148         }
 149         if (!found) {
 150             if (log.isLoggable(PlatformLogger.FINEST)) {
 151                 log.finest("widthHint=" + widthHint + ", heightHint=" + heightHint
 152                            + ", saveWidth=" + saveWidth + ", saveHeight=" + saveHeight
 153                            + ", max_width=" + sizeList[0].get_max_width()
 154                            + ", max_height=" + sizeList[0].get_max_height()
 155                            + ", min_width=" + sizeList[0].get_min_width()
 156                            + ", min_height=" + sizeList[0].get_min_height());
 157             }
 158 
 159             if (widthHint  > sizeList[0].get_max_width() ||
 160                 heightHint > sizeList[0].get_max_height())
 161             {
 162                 // Icon image too big
 163                 /* determine which way to scale */
 164                 int wdiff = widthHint - sizeList[0].get_max_width();
 165                 int hdiff = heightHint - sizeList[0].get_max_height();
 166                 if (log.isLoggable(PlatformLogger.FINEST)) {
 167                     log.finest("wdiff=" + wdiff + ", hdiff=" + hdiff);
 168                 }
 169                 if (wdiff >= hdiff) { /* need to scale width more  */
 170                     saveWidth = sizeList[0].get_max_width();
 171                     saveHeight =
 172                         (int)(((double)sizeList[0].get_max_width()/widthHint) * heightHint);
 173                 } else {
 174                     saveWidth =
 175                         (int)(((double)sizeList[0].get_max_height()/heightHint) * widthHint);
 176                     saveHeight = sizeList[0].get_max_height();
 177                 }
 178             } else if (widthHint  < sizeList[0].get_min_width() ||
 179                        heightHint < sizeList[0].get_min_height())
 180             {
 181                 // Icon image too small
 182                 saveWidth = (sizeList[0].get_min_width()+sizeList[0].get_max_width())/2;
 183                 saveHeight = (sizeList[0].get_min_height()+sizeList[0].get_max_height())/2;
 184             } else {
 185                 // Icon image fits within right size
 186                 saveWidth = widthHint;
 187                 saveHeight = widthHint;
 188             }
 189         }
 190 
 191         XToolkit.awtLock();
 192         try {
 193             XlibWrapper.XFree(sizeList[0].pData);
 194         } finally {
 195             XToolkit.awtUnlock();
 196         }
 197 
 198         if (log.isLoggable(PlatformLogger.FINEST)) {
 199             log.finest("return " + saveWidth + "x" + saveHeight);
 200         }
 201         return new Dimension(saveWidth, saveHeight);
 202     }
 203 
 204     /**
 205      * @return preffered icon size calculated from specific icon
 206      */
 207     Dimension getIconSize(int widthHint, int heightHint) {
 208         if (size == null) {
 209             size = calcIconSize(widthHint, heightHint);
 210         }
 211         return size;
 212     }
 213 
 214    /**
 215     * This function replaces iconPixmap handle with new image
 216     * It does not replace window's hints, so it should be
 217     * called only from setIconImage()
 218     */
 219    void replaceImage(Image img)
 220     {
 221         if (parent == null) {
 222             return;
 223         }
 224         //Prepare image
 225         //create new buffered image of desired size
 226         //in current window's color model
 227         BufferedImage bi = null;
 228         if (img != null && iconWidth != 0 && iconHeight != 0) {
 229             GraphicsConfiguration defaultGC = parent.getGraphicsConfiguration().getDevice().getDefaultConfiguration();
 230             ColorModel model = defaultGC.getColorModel();
 231             WritableRaster raster = model.createCompatibleWritableRaster(iconWidth, iconHeight);
 232             bi = new BufferedImage(model, raster, model.isAlphaPremultiplied(), null);
 233             Graphics g = bi.getGraphics();
 234             try {
 235                 //We need to draw image on SystemColors.window
 236                 //for using as iconWindow's background
 237                 g.setColor(SystemColor.window);
 238                 g.fillRect(0, 0, iconWidth, iconHeight);
 239                 if (g instanceof Graphics2D) {
 240                     ((Graphics2D)g).setComposite(AlphaComposite.Src);
 241                 }
 242                 g.drawImage(img, 0, 0, iconWidth, iconHeight, null);
 243             } finally {
 244                 g.dispose();
 245             }
 246         }
 247         //create pixmap
 248         XToolkit.awtLock();
 249         try {
 250             if (iconPixmap != 0) {
 251                 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap);
 252                 iconPixmap = 0;
 253                 log.finest("Freed previous pixmap");
 254             }
 255             if (bi == null || iconWidth == 0 || iconHeight == 0) {
 256                 return;  //The iconPixmap is 0 now, we have done everything
 257             }
 258             AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData();
 259             awtImageData awtImage = adata.get_awtImage(0);
 260             XVisualInfo visInfo = adata.get_awt_visInfo();
 261             iconPixmap = XlibWrapper.XCreatePixmap(XToolkit.getDisplay(),
 262                                                    XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen()),
 263                                                    iconWidth,
 264                                                    iconHeight,
 265                                                    awtImage.get_Depth()
 266                                                    );
 267             if (iconPixmap == 0) {
 268                 log.finest("Can't create new pixmap for icon");
 269                 return; //Can't do nothing
 270             }
 271             //Transform image data
 272             long bytes = 0;
 273             DataBuffer srcBuf = bi.getData().getDataBuffer();
 274             if (srcBuf instanceof DataBufferByte) {
 275                 byte[] buf = ((DataBufferByte)srcBuf).getData();
 276                 ColorData cdata = adata.get_color_data(0);
 277                 int num_colors = cdata.get_awt_numICMcolors();
 278                 for (int i = 0; i < buf.length; i++) {
 279                     buf[i] = (buf[i] >= num_colors) ?
 280                         0 : cdata.get_awt_icmLUT2Colors(buf[i]);
 281                 }
 282                 bytes = Native.toData(buf);
 283             } else if (srcBuf instanceof DataBufferInt) {
 284                 bytes = Native.toData(((DataBufferInt)srcBuf).getData());
 285             } else if (srcBuf instanceof DataBufferUShort) {
 286                 bytes = Native.toData(((DataBufferUShort)srcBuf).getData());
 287             } else {
 288                 throw new IllegalArgumentException("Unknown data buffer: " + srcBuf);
 289             }
 290             int bpp = awtImage.get_wsImageFormat().get_bits_per_pixel();
 291             int slp =awtImage.get_wsImageFormat().get_scanline_pad();
 292             int bpsl = paddedwidth(iconWidth*bpp, slp) >> 3;
 293             if (((bpsl << 3) / bpp) < iconWidth) {
 294                 log.finest("Image format doesn't fit to icon width");
 295                 return;
 296             }
 297             long dst = XlibWrapper.XCreateImage(XToolkit.getDisplay(),
 298                                                 visInfo.get_visual(),
 299                                                 (int)awtImage.get_Depth(),
 300                                                 (int)XConstants.ZPixmap,
 301                                                 0,
 302                                                 bytes,
 303                                                 iconWidth,
 304                                                 iconHeight,
 305                                                 32,
 306                                                 bpsl);
 307             if (dst == 0) {
 308                 log.finest("Can't create XImage for icon");
 309                 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap);
 310                 iconPixmap = 0;
 311                 return;
 312             } else {
 313                 log.finest("Created XImage for icon");
 314             }
 315             long gc = XlibWrapper.XCreateGC(XToolkit.getDisplay(), iconPixmap, 0, 0);
 316             if (gc == 0) {
 317                 log.finest("Can't create GC for pixmap");
 318                 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap);
 319                 iconPixmap = 0;
 320                 return;
 321             } else {
 322                 log.finest("Created GC for pixmap");
 323             }
 324             try {
 325                 XlibWrapper.XPutImage(XToolkit.getDisplay(), iconPixmap, gc,
 326                                       dst, 0, 0, 0, 0, iconWidth, iconHeight);
 327             } finally {
 328                 XlibWrapper.XFreeGC(XToolkit.getDisplay(), gc);
 329             }
 330         } finally {
 331             XToolkit.awtUnlock();
 332         }
 333     }
 334 
 335    /**
 336     * This function replaces iconPixmap handle with new image
 337     * It does not replace window's hints, so it should be
 338     * called only from setIconImage()
 339     */
 340     void replaceMask(Image img) {
 341         if (parent == null) {
 342             return;
 343         }
 344         //Prepare image
 345         BufferedImage bi = null;
 346         if (img != null && iconWidth != 0 && iconHeight != 0) {
 347             bi = new BufferedImage(iconWidth, iconHeight, BufferedImage.TYPE_INT_ARGB);
 348             Graphics g = bi.getGraphics();
 349             try {
 350                 g.drawImage(img, 0, 0, iconWidth, iconHeight, null);
 351             } finally {
 352                 g.dispose();
 353             }
 354         }
 355         //create mask
 356         XToolkit.awtLock();
 357         try {
 358             if (iconMask != 0) {
 359                 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconMask);
 360                 iconMask = 0;
 361                 log.finest("Freed previous mask");
 362             }
 363             if (bi == null || iconWidth == 0 || iconHeight == 0) {
 364                 return;  //The iconMask is 0 now, we have done everything
 365             }
 366             AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData();
 367             awtImageData awtImage = adata.get_awtImage(0);
 368             XVisualInfo visInfo = adata.get_awt_visInfo();
 369             ColorModel cm = bi.getColorModel();
 370             DataBuffer srcBuf = bi.getRaster().getDataBuffer();
 371             int sidx = 0;//index of source element
 372             int bpl = (iconWidth + 7) >> 3;//bytes per line
 373             byte[] destBuf = new byte[bpl * iconHeight];
 374             int didx = 0;//index of destination element
 375             for (int i = 0; i < iconHeight; i++) {
 376                 int dbit = 0;//index of current bit
 377                 int cv = 0;
 378                 for (int j = 0; j < iconWidth; j++) {
 379                     if (cm.getAlpha(srcBuf.getElem(sidx)) != 0 ) {
 380                         cv = cv + (1 << dbit);
 381                     }
 382                     dbit++;
 383                     if (dbit == 8) {
 384                         destBuf[didx] = (byte)cv;
 385                         cv = 0;
 386                         dbit = 0;
 387                         didx++;
 388                     }
 389                     sidx++;
 390                 }
 391             }
 392             iconMask = XlibWrapper.XCreateBitmapFromData(XToolkit.getDisplay(),
 393                 XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen()),
 394                 Native.toData(destBuf),
 395                 iconWidth, iconHeight);
 396         } finally {
 397             XToolkit.awtUnlock();
 398         }
 399     }
 400 
 401     /**
 402      * Sets icon image by selecting one of the images from the list.
 403      * The selected image is the one having the best matching size.
 404      */
 405     void setIconImages(java.util.List<XIconInfo> icons) {
 406         if (icons == null || icons.size() == 0) return;
 407 
 408         int minDiff = Integer.MAX_VALUE;
 409         Image min = null;
 410         for (XIconInfo iconInfo : icons) {
 411             if (iconInfo.isValid()) {
 412                 Image image = iconInfo.getImage();
 413                 Dimension dim = calcIconSize(image.getWidth(null), image.getHeight(null));
 414                 int widthDiff = Math.abs(dim.width - image.getWidth(null));
 415                 int heightDiff = Math.abs(image.getHeight(null) - dim.height);
 416 
 417                 // "=" below allows to select the best matching icon
 418                 if (minDiff >= (widthDiff + heightDiff)) {
 419                     minDiff = (widthDiff + heightDiff);
 420                     min = image;
 421                 }
 422             }
 423         }
 424         if (min != null) {
 425             log.finer("Icon: {0}x{1}", min.getWidth(null), min.getHeight(null));
 426             setIconImage(min);
 427         }
 428     }
 429 
 430     void setIconImage(Image img) {
 431         if (img == null) {
 432             //if image is null, reset to default image
 433             replaceImage(null);
 434             replaceMask(null);
 435         } else {
 436             //get image size
 437             int width;
 438             int height;
 439             if (img instanceof ToolkitImage) {
 440                 ImageRepresentation ir = ((ToolkitImage)img).getImageRep();
 441                 ir.reconstruct(ImageObserver.ALLBITS);
 442                 width = ir.getWidth();
 443                 height = ir.getHeight();
 444             }
 445             else {
 446                 width = img.getWidth(null);
 447                 height = img.getHeight(null);
 448             }
 449             Dimension iconSize = getIconSize(width, height);
 450             if (iconSize != null) {
 451                 log.finest("Icon size: {0}", iconSize);
 452                 iconWidth = iconSize.width;
 453                 iconHeight = iconSize.height;
 454             } else {
 455                 log.finest("Error calculating image size");
 456                 iconWidth = 0;
 457                 iconHeight = 0;
 458             }
 459             replaceImage(img);
 460             replaceMask(img);
 461         }
 462         //create icon window and set XWMHints
 463         XToolkit.awtLock();
 464         try {
 465             AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData();
 466             awtImageData awtImage = adata.get_awtImage(0);
 467             XVisualInfo visInfo = adata.get_awt_visInfo();
 468             XWMHints hints = parent.getWMHints();
 469             window = hints.get_icon_window();
 470             if (window == 0) {
 471                 log.finest("Icon window wasn't set");
 472                 XCreateWindowParams params = getDelayedParams();
 473                 params.add(BORDER_PIXEL, Long.valueOf(XToolkit.getAwtDefaultFg()));
 474                 params.add(BACKGROUND_PIXMAP, iconPixmap);
 475                 params.add(COLORMAP, adata.get_awt_cmap());
 476                 params.add(DEPTH, awtImage.get_Depth());
 477                 params.add(VISUAL_CLASS, (int)XConstants.InputOutput);
 478                 params.add(VISUAL, visInfo.get_visual());
 479                 params.add(VALUE_MASK, XConstants.CWBorderPixel | XConstants.CWColormap | XConstants.CWBackPixmap);
 480                 params.add(PARENT_WINDOW, XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen()));
 481                 params.add(BOUNDS, new Rectangle(0, 0, iconWidth, iconHeight));
 482                 params.remove(DELAYED);
 483                 init(params);
 484                 if (getWindow() == 0) {
 485                     log.finest("Can't create new icon window");
 486                 } else {
 487                     log.finest("Created new icon window");
 488                 }
 489             }
 490             if (getWindow() != 0) {
 491                 XlibWrapper.XSetWindowBackgroundPixmap(XToolkit.getDisplay(), getWindow(), iconPixmap);
 492                 XlibWrapper.XClearWindow(XToolkit.getDisplay(), getWindow());
 493             }
 494             // Provide both pixmap and window, WM or Taskbar will use the one they find more appropriate
 495             long newFlags = hints.get_flags() | XUtilConstants.IconPixmapHint | XUtilConstants.IconMaskHint;
 496             if (getWindow()  != 0) {
 497                 newFlags |= XUtilConstants.IconWindowHint;
 498             }
 499             hints.set_flags(newFlags);
 500             hints.set_icon_pixmap(iconPixmap);
 501             hints.set_icon_mask(iconMask);
 502             hints.set_icon_window(getWindow());
 503             XlibWrapper.XSetWMHints(XToolkit.getDisplay(), parent.getShell(), hints.pData);
 504             log.finest("Set icon window hint");
 505         } finally {
 506             XToolkit.awtUnlock();
 507         }
 508     }
 509 
 510     static int paddedwidth(int number, int boundary)
 511     {
 512         return (((number) + ((boundary) - 1)) & (~((boundary) - 1)));
 513     }
 514 }
--- EOF ---