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