1 /* 2 * Copyright (c) 2003, 2013, 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 final static 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 buf[i] = (buf[i] >= num_colors) ? 285 0 : cdata.get_awt_icmLUT2Colors(buf[i]); 286 } 287 bytes = Native.toData(buf); 288 } else if (srcBuf instanceof DataBufferInt) { 289 bytes = Native.toData(((DataBufferInt)srcBuf).getData()); 290 } else if (srcBuf instanceof DataBufferUShort) { 291 bytes = Native.toData(((DataBufferUShort)srcBuf).getData()); 292 } else { 293 throw new IllegalArgumentException("Unknown data buffer: " + srcBuf); 294 } 295 int bpp = awtImage.get_wsImageFormat().get_bits_per_pixel(); 296 int slp =awtImage.get_wsImageFormat().get_scanline_pad(); 297 int bpsl = paddedwidth(iconWidth*bpp, slp) >> 3; 298 if (((bpsl << 3) / bpp) < iconWidth) { 299 log.finest("Image format doesn't fit to icon width"); 300 return; 301 } 302 long dst = XlibWrapper.XCreateImage(XToolkit.getDisplay(), 303 visInfo.get_visual(), 304 (int)awtImage.get_Depth(), 305 (int)XConstants.ZPixmap, 306 0, 307 bytes, 308 iconWidth, 309 iconHeight, 310 32, 311 bpsl); 312 if (dst == 0) { 313 log.finest("Can't create XImage for icon"); 314 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap); 315 iconPixmap = 0; 316 return; 317 } else { 318 log.finest("Created XImage for icon"); 319 } 320 long gc = XlibWrapper.XCreateGC(XToolkit.getDisplay(), iconPixmap, 0, 0); 321 if (gc == 0) { 322 log.finest("Can't create GC for pixmap"); 323 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconPixmap); 324 iconPixmap = 0; 325 return; 326 } else { 327 log.finest("Created GC for pixmap"); 328 } 329 try { 330 XlibWrapper.XPutImage(XToolkit.getDisplay(), iconPixmap, gc, 331 dst, 0, 0, 0, 0, iconWidth, iconHeight); 332 } finally { 333 XlibWrapper.XFreeGC(XToolkit.getDisplay(), gc); 334 } 335 } finally { 336 XToolkit.awtUnlock(); 337 } 338 } 339 340 /** 341 * This function replaces iconPixmap handle with new image 342 * It does not replace window's hints, so it should be 343 * called only from setIconImage() 344 */ 345 void replaceMask(Image img) { 346 if (parent == null) { 347 return; 348 } 349 //Prepare image 350 BufferedImage bi = null; 351 if (img != null && iconWidth != 0 && iconHeight != 0) { 352 bi = new BufferedImage(iconWidth, iconHeight, BufferedImage.TYPE_INT_ARGB); 353 Graphics g = bi.getGraphics(); 354 try { 355 g.drawImage(img, 0, 0, iconWidth, iconHeight, null); 356 } finally { 357 g.dispose(); 358 } 359 } 360 //create mask 361 XToolkit.awtLock(); 362 try { 363 if (iconMask != 0) { 364 XlibWrapper.XFreePixmap(XToolkit.getDisplay(), iconMask); 365 iconMask = 0; 366 log.finest("Freed previous mask"); 367 } 368 if (bi == null || iconWidth == 0 || iconHeight == 0) { 369 return; //The iconMask is 0 now, we have done everything 370 } 371 AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData(); 372 awtImageData awtImage = adata.get_awtImage(0); 373 XVisualInfo visInfo = adata.get_awt_visInfo(); 374 ColorModel cm = bi.getColorModel(); 375 DataBuffer srcBuf = bi.getRaster().getDataBuffer(); 376 int sidx = 0;//index of source element 377 int bpl = (iconWidth + 7) >> 3;//bytes per line 378 byte[] destBuf = new byte[bpl * iconHeight]; 379 int didx = 0;//index of destination element 380 for (int i = 0; i < iconHeight; i++) { 381 int dbit = 0;//index of current bit 382 int cv = 0; 383 for (int j = 0; j < iconWidth; j++) { 384 if (cm.getAlpha(srcBuf.getElem(sidx)) != 0 ) { 385 cv = cv + (1 << dbit); 386 } 387 dbit++; 388 if (dbit == 8) { 389 destBuf[didx] = (byte)cv; 390 cv = 0; 391 dbit = 0; 392 didx++; 393 } 394 sidx++; 395 } 396 } 397 iconMask = XlibWrapper.XCreateBitmapFromData(XToolkit.getDisplay(), 398 XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen()), 399 Native.toData(destBuf), 400 iconWidth, iconHeight); 401 } finally { 402 XToolkit.awtUnlock(); 403 } 404 } 405 406 /** 407 * Sets icon image by selecting one of the images from the list. 408 * The selected image is the one having the best matching size. 409 */ 410 void setIconImages(java.util.List<IconInfo> icons) { 411 if (icons == null || icons.size() == 0) return; 412 413 int minDiff = Integer.MAX_VALUE; 414 Image min = null; 415 for (IconInfo iconInfo : icons) { 416 if (iconInfo.isValid()) { 417 Image image = iconInfo.getImage(); 418 Dimension dim = calcIconSize(image.getWidth(null), image.getHeight(null)); 419 int widthDiff = Math.abs(dim.width - image.getWidth(null)); 420 int heightDiff = Math.abs(image.getHeight(null) - dim.height); 421 422 // "=" below allows to select the best matching icon 423 if (minDiff >= (widthDiff + heightDiff)) { 424 minDiff = (widthDiff + heightDiff); 425 min = image; 426 } 427 } 428 } 429 if (min != null) { 430 if (log.isLoggable(PlatformLogger.Level.FINER)) { 431 log.finer("Icon: {0}x{1}", min.getWidth(null), min.getHeight(null)); 432 } 433 setIconImage(min); 434 } 435 } 436 437 void setIconImage(Image img) { 438 if (img == null) { 439 //if image is null, reset to default image 440 replaceImage(null); 441 replaceMask(null); 442 } else { 443 //get image size 444 int width; 445 int height; 446 if (img instanceof ToolkitImage) { 447 ImageRepresentation ir = ((ToolkitImage)img).getImageRep(); 448 ir.reconstruct(ImageObserver.ALLBITS); 449 width = ir.getWidth(); 450 height = ir.getHeight(); 451 } 452 else { 453 width = img.getWidth(null); 454 height = img.getHeight(null); 455 } 456 Dimension iconSize = getIconSize(width, height); 457 if (iconSize != null) { 458 if (log.isLoggable(PlatformLogger.Level.FINEST)) { 459 log.finest("Icon size: {0}", iconSize); 460 } 461 iconWidth = iconSize.width; 462 iconHeight = iconSize.height; 463 } else { 464 log.finest("Error calculating image size"); 465 iconWidth = 0; 466 iconHeight = 0; 467 } 468 replaceImage(img); 469 replaceMask(img); 470 } 471 //create icon window and set XWMHints 472 XToolkit.awtLock(); 473 try { 474 AwtGraphicsConfigData adata = parent.getGraphicsConfigurationData(); 475 awtImageData awtImage = adata.get_awtImage(0); 476 XVisualInfo visInfo = adata.get_awt_visInfo(); 477 XWMHints hints = parent.getWMHints(); 478 window = hints.get_icon_window(); 479 if (window == 0) { 480 log.finest("Icon window wasn't set"); 481 XCreateWindowParams params = getDelayedParams(); 482 params.add(BORDER_PIXEL, Long.valueOf(XToolkit.getAwtDefaultFg())); 483 params.add(BACKGROUND_PIXMAP, iconPixmap); 484 params.add(COLORMAP, adata.get_awt_cmap()); 485 params.add(DEPTH, awtImage.get_Depth()); 486 params.add(VISUAL_CLASS, (int)XConstants.InputOutput); 487 params.add(VISUAL, visInfo.get_visual()); 488 params.add(VALUE_MASK, XConstants.CWBorderPixel | XConstants.CWColormap | XConstants.CWBackPixmap); 489 params.add(PARENT_WINDOW, XlibWrapper.RootWindow(XToolkit.getDisplay(), visInfo.get_screen())); 490 params.add(BOUNDS, new Rectangle(0, 0, iconWidth, iconHeight)); 491 params.remove(DELAYED); 492 init(params); 493 if (getWindow() == 0) { 494 log.finest("Can't create new icon window"); 495 } else { 496 log.finest("Created new icon window"); 497 } 498 } 499 if (getWindow() != 0) { 500 XlibWrapper.XSetWindowBackgroundPixmap(XToolkit.getDisplay(), getWindow(), iconPixmap); 501 XlibWrapper.XClearWindow(XToolkit.getDisplay(), getWindow()); 502 } 503 // Provide both pixmap and window, WM or Taskbar will use the one they find more appropriate 504 long newFlags = hints.get_flags() | XUtilConstants.IconPixmapHint | XUtilConstants.IconMaskHint; 505 if (getWindow() != 0) { 506 newFlags |= XUtilConstants.IconWindowHint; 507 } 508 hints.set_flags(newFlags); 509 hints.set_icon_pixmap(iconPixmap); 510 hints.set_icon_mask(iconMask); 511 hints.set_icon_window(getWindow()); 512 XlibWrapper.XSetWMHints(XToolkit.getDisplay(), parent.getShell(), hints.pData); 513 log.finest("Set icon window hint"); 514 } finally { 515 XToolkit.awtUnlock(); 516 } 517 } 518 519 static int paddedwidth(int number, int boundary) 520 { 521 return (((number) + ((boundary) - 1)) & (~((boundary) - 1))); 522 } 523 }