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;
XIconWindow(XDecoratedPeer parent)44     XIconWindow(XDecoratedPeer parent) {
45         super(new XCreateWindowParams(new Object[] {
46             PARENT, parent,
47             DELAYED, Boolean.TRUE}));
48     }
49 
instantPreInit(XCreateWindowParams params)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      */
getIconSizes()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 
calcIconSize(int widthHint, int heightHint)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      */
getIconSize(int widthHint, int heightHint)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     */
replaceImage(Image img)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     */
replaceMask(Image img)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      */
setIconImages(java.util.List<IconInfo> icons)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 
setIconImage(Image img)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 
paddedwidth(int number, int boundary)520     static int paddedwidth(int number, int boundary)
521     {
522         return (((number) + ((boundary) - 1)) & (~((boundary) - 1)));
523     }
524 }
525