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