/*
 * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.awt.X11;

import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.SystemColor;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.ImageObserver;
import java.awt.image.WritableRaster;

import sun.awt.IconInfo;
import sun.awt.image.ImageRepresentation;
import sun.awt.image.ToolkitImage;
import sun.util.logging.PlatformLogger;

public class XIconWindow extends XBaseWindow {
    private final static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XIconWindow");
    XDecoratedPeer parent;
    Dimension size;
    long iconPixmap = 0;
    long iconMask = 0;
    int iconWidth = 0;
    int iconHeight = 0;
    XIconWindow(XDecoratedPeer parent) {
        super(new XCreateWindowParams(new Object[] {
            PARENT, parent,
            DELAYED, Boolean.TRUE}));
    }

    void instantPreInit(XCreateWindowParams params) {
        super.instantPreInit(params);
        this.parent = (XDecoratedPeer)params.get(PARENT);
    }

    
Returns:array of XIconsSize structures, caller must free this array after use.
/** * @return array of XIconsSize structures, caller must free this array after use. */
private XIconSize[] getIconSizes() { XToolkit.awtLock(); try { AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData(); final long screen = adata.get_awt_visInfo().get_screen(); final long display = XToolkit.getDisplay(); if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest(adata.toString()); } long status = XlibWrapper.XGetIconSizes(display, XToolkit.getDefaultRootWindow(), XlibWrapper.larg1, XlibWrapper.iarg1); if (status == 0) { return null; } int count = Native.getInt(XlibWrapper.iarg1); long sizes_ptr = Native.getLong(XlibWrapper.larg1); // XIconSize* if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("count = {1}, sizes_ptr = {0}", Long.valueOf(sizes_ptr), Integer.valueOf(count)); } XIconSize[] res = new XIconSize[count]; for (int i = 0; i < count; i++, sizes_ptr += XIconSize.getSize()) { res[i] = new XIconSize(sizes_ptr); if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("sizes_ptr[{1}] = {0}", res[i], Integer.valueOf(i)); } } return res; } finally { XToolkit.awtUnlock(); } } private Dimension calcIconSize(int widthHint, int heightHint) { if (XWM.getWMID() == XWM.ICE_WM) { // ICE_WM has a bug - it only displays icons of the size // 16x16, while reporting 32x32 in its size list log.finest("Returning ICE_WM icon size: 16x16"); return new Dimension(16, 16); } XIconSize[] sizeList = getIconSizes(); if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("Icon sizes: {0}", (Object[]) sizeList); } if (sizeList == null) { // No icon sizes so we simply fall back to 16x16 return new Dimension(16, 16); } boolean found = false; int dist = 0xffffffff, newDist, diff = 0, closestHeight, closestWidth; int saveWidth = 0, saveHeight = 0; for (int i = 0; i < sizeList.length; i++) { if (widthHint >= sizeList[i].get_min_width() && widthHint <= sizeList[i].get_max_width() && heightHint >= sizeList[i].get_min_height() && heightHint <= sizeList[i].get_max_height()) { found = true; if ((((widthHint-sizeList[i].get_min_width()) % sizeList[i].get_width_inc()) == 0) && (((heightHint-sizeList[i].get_min_height()) % sizeList[i].get_height_inc()) ==0)) { /* Found an exact match */ saveWidth = widthHint; saveHeight = heightHint; dist = 0; break; } diff = widthHint - sizeList[i].get_min_width(); if (diff == 0) { closestWidth = widthHint; } else { diff = diff%sizeList[i].get_width_inc(); closestWidth = widthHint - diff; } diff = heightHint - sizeList[i].get_min_height(); if (diff == 0) { closestHeight = heightHint; } else { diff = diff%sizeList[i].get_height_inc(); closestHeight = heightHint - diff; } newDist = closestWidth*closestWidth + closestHeight*closestHeight; if (dist > newDist) { saveWidth = closestWidth; saveHeight = closestHeight; dist = newDist; } } } if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("found=" + found); } if (!found) { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("widthHint=" + widthHint + ", heightHint=" + heightHint + ", saveWidth=" + saveWidth + ", saveHeight=" + saveHeight + ", max_width=" + sizeList[0].get_max_width() + ", max_height=" + sizeList[0].get_max_height() + ", min_width=" + sizeList[0].get_min_width() + ", min_height=" + sizeList[0].get_min_height()); } if (widthHint > sizeList[0].get_max_width() || heightHint > sizeList[0].get_max_height()) { // Icon image too big /* determine which way to scale */ int wdiff = widthHint - sizeList[0].get_max_width(); int hdiff = heightHint - sizeList[0].get_max_height(); if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("wdiff=" + wdiff + ", hdiff=" + hdiff); } if (wdiff >= hdiff) { /* need to scale width more */ saveWidth = sizeList[0].get_max_width(); saveHeight = (int)(((double)sizeList[0].get_max_width()/widthHint) * heightHint); } else { saveWidth = (int)(((double)sizeList[0].get_max_height()/heightHint) * widthHint); saveHeight = sizeList[0].get_max_height(); } } else if (widthHint < sizeList[0].get_min_width() || heightHint < sizeList[0].get_min_height()) { // Icon image too small saveWidth = (sizeList[0].get_min_width()+sizeList[0].get_max_width())/2; saveHeight = (sizeList[0].get_min_height()+sizeList[0].get_max_height())/2; } else { // Icon image fits within right size saveWidth = widthHint; saveHeight = widthHint; } } XToolkit.awtLock(); try { XlibWrapper.XFree(sizeList[0].pData); } finally { XToolkit.awtUnlock(); } if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("return " + saveWidth + "x" + saveHeight); } return new Dimension(saveWidth, saveHeight); }
Returns:preffered icon size calculated from specific icon
/** * @return preffered icon size calculated from specific icon */
Dimension getIconSize(int widthHint, int heightHint) { if (size == null) { size = calcIconSize(widthHint, heightHint); } return size; }
This function replaces iconPixmap handle with new image It does not replace window's hints, so it should be called only from setIconImage()
/** * This function replaces iconPixmap handle with new image * It does not replace window's hints, so it should be * called only from setIconImage() */
void replaceImage(Image img) { if (parent == null) { return; } //Prepare image //create new buffered image of desired size //in current window's color model BufferedImage bi = null; if (img != null && iconWidth != 0 && iconHeight != 0) { GraphicsConfiguration defaultGC = parent.getGraphicsConfiguration().getDevice().getDefaultConfiguration(); ColorModel model = defaultGC.getColorModel(); WritableRaster raster = model.createCompatibleWritableRaster(iconWidth, iconHeight); bi = new BufferedImage(model, raster, model.isAlphaPremultiplied(), null); Graphics g = bi.getGraphics(); try { //We need to draw image on SystemColors.window //for using as iconWindow's background g.setColor(SystemColor.window); g.fillRect(0, 0, iconWidth, iconHeight); if (g instanceof Graphics2D) { ((Graphics2D)g).setComposite(AlphaComposite.Src); } g.drawImage(img, 0, 0, iconWidth, iconHeight, null); } finally { g.dispose(); } } //create pixmap XToolkit.awtLock(); try { if (iconPixmap != 0) { XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap); iconPixmap = 0; log.finest("Freed previous pixmap"); } if (bi == null || iconWidth == 0 || iconHeight == 0) { return; //The iconPixmap is 0 now, we have done everything } AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData(); awtImageData awtImage = adata.get_awtImage(0); XVisualInfo visInfo = adata.get_awt_visInfo(); iconPixmap = XlibWrapper.XCreatePixmap(XToolkit.getDisplay(), XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen()), iconWidth, iconHeight, awtImage.get_Depth() ); if (iconPixmap == 0) { log.finest("Can't create new pixmap for icon"); return; //Can't do nothing } //Transform image data long bytes = 0; DataBuffer srcBuf = bi.getData().getDataBuffer(); if (srcBuf instanceof DataBufferByte) { byte[] buf = ((DataBufferByte)srcBuf).getData(); ColorData cdata = adata.get_color_data(0); int num_colors = cdata.get_awt_numICMcolors(); for (int i = 0; i < buf.length; i++) { buf[i] = (buf[i] >= num_colors) ? 0 : cdata.get_awt_icmLUT2Colors(buf[i]); } bytes = Native.toData(buf); } else if (srcBuf instanceof DataBufferInt) { bytes = Native.toData(((DataBufferInt)srcBuf).getData()); } else if (srcBuf instanceof DataBufferUShort) { bytes = Native.toData(((DataBufferUShort)srcBuf).getData()); } else { throw new IllegalArgumentException("Unknown data buffer: " + srcBuf); } int bpp = awtImage.get_wsImageFormat().get_bits_per_pixel(); int slp =awtImage.get_wsImageFormat().get_scanline_pad(); int bpsl = paddedwidth(iconWidth*bpp, slp) >> 3; if (((bpsl << 3) / bpp) < iconWidth) { log.finest("Image format doesn't fit to icon width"); return; } long dst = XlibWrapper.XCreateImage(XToolkit.getDisplay(), visInfo.get_visual(), (int)awtImage.get_Depth(), (int)XConstants.ZPixmap, 0, bytes, iconWidth, iconHeight, 32, bpsl); if (dst == 0) { log.finest("Can't create XImage for icon"); XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap); iconPixmap = 0; return; } else { log.finest("Created XImage for icon"); } long gc = XlibWrapper.XCreateGC(XToolkit.getDisplay(), iconPixmap, 0, 0); if (gc == 0) { log.finest("Can't create GC for pixmap"); XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap); iconPixmap = 0; return; } else { log.finest("Created GC for pixmap"); } try { XlibWrapper.XPutImage(XToolkit.getDisplay(), iconPixmap, gc, dst, 0, 0, 0, 0, iconWidth, iconHeight); } finally { XlibWrapper.XFreeGC(XToolkit.getDisplay(), gc); } } finally { XToolkit.awtUnlock(); } }
This function replaces iconPixmap handle with new image It does not replace window's hints, so it should be called only from setIconImage()
/** * This function replaces iconPixmap handle with new image * It does not replace window's hints, so it should be * called only from setIconImage() */
void replaceMask(Image img) { if (parent == null) { return; } //Prepare image BufferedImage bi = null; if (img != null && iconWidth != 0 && iconHeight != 0) { bi = new BufferedImage(iconWidth, iconHeight, BufferedImage.TYPE_INT_ARGB); Graphics g = bi.getGraphics(); try { g.drawImage(img, 0, 0, iconWidth, iconHeight, null); } finally { g.dispose(); } } //create mask XToolkit.awtLock(); try { if (iconMask != 0) { XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconMask); iconMask = 0; log.finest("Freed previous mask"); } if (bi == null || iconWidth == 0 || iconHeight == 0) { return; //The iconMask is 0 now, we have done everything } AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData(); awtImageData awtImage = adata.get_awtImage(0); XVisualInfo visInfo = adata.get_awt_visInfo(); ColorModel cm = bi.getColorModel(); DataBuffer srcBuf = bi.getRaster().getDataBuffer(); int sidx = 0;//index of source element int bpl = (iconWidth + 7) >> 3;//bytes per line byte[] destBuf = new byte[bpl * iconHeight]; int didx = 0;//index of destination element for (int i = 0; i < iconHeight; i++) { int dbit = 0;//index of current bit int cv = 0; for (int j = 0; j < iconWidth; j++) { if (cm.getAlpha(srcBuf.getElem(sidx)) != 0 ) { cv = cv + (1 << dbit); } dbit++; if (dbit == 8) { destBuf[didx] = (byte)cv; cv = 0; dbit = 0; didx++; } sidx++; } } iconMask = XlibWrapper.XCreateBitmapFromData(XToolkit.getDisplay(), XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen()), Native.toData(destBuf), iconWidth, iconHeight); } finally { XToolkit.awtUnlock(); } }
Sets icon image by selecting one of the images from the list. The selected image is the one having the best matching size.
/** * Sets icon image by selecting one of the images from the list. * The selected image is the one having the best matching size. */
void setIconImages(java.util.List<IconInfo> icons) { if (icons == null || icons.size() == 0) return; int minDiff = Integer.MAX_VALUE; Image min = null; for (IconInfo iconInfo : icons) { if (iconInfo.isValid()) { Image image = iconInfo.getImage(); Dimension dim = calcIconSize(image.getWidth(null), image.getHeight(null)); int widthDiff = Math.abs(dim.width - image.getWidth(null)); int heightDiff = Math.abs(image.getHeight(null) - dim.height); // "=" below allows to select the best matching icon if (minDiff >= (widthDiff + heightDiff)) { minDiff = (widthDiff + heightDiff); min = image; } } } if (min != null) { if (log.isLoggable(PlatformLogger.Level.FINER)) { log.finer("Icon: {0}x{1}", min.getWidth(null), min.getHeight(null)); } setIconImage(min); } } void setIconImage(Image img) { if (img == null) { //if image is null, reset to default image replaceImage(null); replaceMask(null); } else { //get image size int width; int height; if (img instanceof ToolkitImage) { ImageRepresentation ir = ((ToolkitImage)img).getImageRep(); ir.reconstruct(ImageObserver.ALLBITS); width = ir.getWidth(); height = ir.getHeight(); } else { width = img.getWidth(null); height = img.getHeight(null); } Dimension iconSize = getIconSize(width, height); if (iconSize != null) { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("Icon size: {0}", iconSize); } iconWidth = iconSize.width; iconHeight = iconSize.height; } else { log.finest("Error calculating image size"); iconWidth = 0; iconHeight = 0; } replaceImage(img); replaceMask(img); } //create icon window and set XWMHints XToolkit.awtLock(); try { AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData(); awtImageData awtImage = adata.get_awtImage(0); XVisualInfo visInfo = adata.get_awt_visInfo(); XWMHints hints = parent.getWMHints(); window = hints.get_icon_window(); if (window == 0) { log.finest("Icon window wasn't set"); XCreateWindowParams params = getDelayedParams(); params.add(BORDER_PIXEL, Long.valueOf(0)); params.add(BACKGROUND_PIXMAP, iconPixmap); params.add(COLORMAP, adata.get_awt_cmap()); params.add(DEPTH, awtImage.get_Depth()); params.add(VISUAL_CLASS, (int)XConstants.InputOutput); params.add(VISUAL, visInfo.get_visual()); params.add(VALUE_MASK, XConstants.CWBorderPixel | XConstants.CWColormap | XConstants.CWBackPixmap); params.add(PARENT_WINDOW, XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen())); params.add(BOUNDS, new Rectangle(0, 0, iconWidth, iconHeight)); params.remove(DELAYED); init(params); if (getWindow() == 0) { log.finest("Can't create new icon window"); } else { log.finest("Created new icon window"); } } if (getWindow() != 0) { XlibWrapper.XSetWindowBackgroundPixmap(XToolkit.getDisplay(), getWindow(), iconPixmap); XlibWrapper.XClearWindow(XToolkit.getDisplay(), getWindow()); } // Provide both pixmap and window, WM or Taskbar will use the one they find more appropriate long newFlags = hints.get_flags() | XUtilConstants.IconPixmapHint | XUtilConstants.IconMaskHint; if (getWindow() != 0) { newFlags |= XUtilConstants.IconWindowHint; } hints.set_flags(newFlags); hints.set_icon_pixmap(iconPixmap); hints.set_icon_mask(iconMask); hints.set_icon_window(getWindow()); XlibWrapper.XSetWMHints(XToolkit.getDisplay(), parent.getShell(), hints.pData); log.finest("Set icon window hint"); } finally { XToolkit.awtUnlock(); } } static int paddedwidth(int number, int boundary) { return (((number) + ((boundary) - 1)) & (~((boundary) - 1))); } }