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