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