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.geom.Point2D;
  29 import java.lang.ref.WeakReference;
  30 
  31 import sun.awt.IconInfo;
  32 import sun.awt.AWTAccessor;
  33 import sun.awt.SunToolkit;
  34 
  35 class XWarningWindow extends XWindow {
  36     private static final int SHOWING_DELAY = 330;
  37     private static final int HIDING_DELAY = 2000;
  38 
  39     private final Window ownerWindow;
  40     private WeakReference<XWindowPeer> ownerPeer;
  41     private long parentWindow;
  42 
  43     private static final String OWNER = "OWNER";
  44     private InfoWindow.Tooltip tooltip;
  45 
  46     /**
  47      * Animation stage.
  48      */
  49     private volatile int currentIcon = 0;
  50 
  51     /* -1 - uninitialized.
  52      * 0 - 16x16
  53      * 1 - 24x24
  54      * 2 - 32x32
  55      * 3 - 48x48
  56      */
  57     private int currentSize = -1;
  58     private static IconInfo[][] icons;
  59     private static IconInfo getSecurityIconInfo(int size, int num) {
  60         synchronized (XWarningWindow.class) {
  61             if (icons == null) {
  62                 icons = new IconInfo[4][3];
  63                 if (XlibWrapper.dataModel == 32) {
  64                     icons[0][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw16_png.security_icon_bw16_png);
  65                     icons[0][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim16_png.security_icon_interim16_png);
  66                     icons[0][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow16_png.security_icon_yellow16_png);
  67                     icons[1][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw24_png.security_icon_bw24_png);
  68                     icons[1][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim24_png.security_icon_interim24_png);
  69                     icons[1][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow24_png.security_icon_yellow24_png);
  70                     icons[2][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw32_png.security_icon_bw32_png);
  71                     icons[2][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim32_png.security_icon_interim32_png);
  72                     icons[2][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow32_png.security_icon_yellow32_png);
  73                     icons[3][0] = new IconInfo(sun.awt.AWTIcon32_security_icon_bw48_png.security_icon_bw48_png);
  74                     icons[3][1] = new IconInfo(sun.awt.AWTIcon32_security_icon_interim48_png.security_icon_interim48_png);
  75                     icons[3][2] = new IconInfo(sun.awt.AWTIcon32_security_icon_yellow48_png.security_icon_yellow48_png);
  76                 } else {
  77                     icons[0][0] = new IconInfo(sun.awt.AWTIcon64_security_icon_bw16_png.security_icon_bw16_png);
  78                     icons[0][1] = new IconInfo(sun.awt.AWTIcon64_security_icon_interim16_png.security_icon_interim16_png);
  79                     icons[0][2] = new IconInfo(sun.awt.AWTIcon64_security_icon_yellow16_png.security_icon_yellow16_png);
  80                     icons[1][0] = new IconInfo(sun.awt.AWTIcon64_security_icon_bw24_png.security_icon_bw24_png);
  81                     icons[1][1] = new IconInfo(sun.awt.AWTIcon64_security_icon_interim24_png.security_icon_interim24_png);
  82                     icons[1][2] = new IconInfo(sun.awt.AWTIcon64_security_icon_yellow24_png.security_icon_yellow24_png);
  83                     icons[2][0] = new IconInfo(sun.awt.AWTIcon64_security_icon_bw32_png.security_icon_bw32_png);
  84                     icons[2][1] = new IconInfo(sun.awt.AWTIcon64_security_icon_interim32_png.security_icon_interim32_png);
  85                     icons[2][2] = new IconInfo(sun.awt.AWTIcon64_security_icon_yellow32_png.security_icon_yellow32_png);
  86                     icons[3][0] = new IconInfo(sun.awt.AWTIcon64_security_icon_bw48_png.security_icon_bw48_png);
  87                     icons[3][1] = new IconInfo(sun.awt.AWTIcon64_security_icon_interim48_png.security_icon_interim48_png);
  88                     icons[3][2] = new IconInfo(sun.awt.AWTIcon64_security_icon_yellow48_png.security_icon_yellow48_png);
  89                 }
  90             }
  91         }
  92         final int sizeIndex = size % icons.length;
  93         return icons[sizeIndex][num % icons[sizeIndex].length];
  94     }
  95 
  96     private void updateIconSize() {
  97         int newSize = -1;
  98 
  99         if (ownerWindow != null) {
 100             Insets insets = ownerWindow.getInsets();
 101             int max = Math.max(insets.top, Math.max(insets.bottom,
 102                         Math.max(insets.left, insets.right)));
 103             if (max < 24) {
 104                 newSize = 0;
 105             } else if (max < 32) {
 106                 newSize = 1;
 107             } else if (max < 48) {
 108                 newSize = 2;
 109             } else {
 110                 newSize = 3;
 111             }
 112         }
 113         // Make sure we have a valid size
 114         if (newSize == -1) {
 115             newSize = 0;
 116         }
 117 
 118         // Note: this is not the most wise solution to use awtLock here,
 119         // this should have been sync'ed with the stateLock. However,
 120         // the awtLock must be taken first (see XBaseWindow.getStateLock()),
 121         // and we need the awtLock anyway to update the shape of the icon.
 122         // So it's easier to use just one lock instead.
 123         XToolkit.awtLock();
 124         try {
 125             if (newSize != currentSize) {
 126                 currentSize = newSize;
 127                 IconInfo ico = getSecurityIconInfo(currentSize, 0);
 128                 XlibWrapper.SetBitmapShape(XToolkit.getDisplay(), getWindow(),
 129                         ico.getWidth(), ico.getHeight(), ico.getIntData());
 130                 AWTAccessor.getWindowAccessor().setSecurityWarningSize(
 131                         ownerWindow, ico.getWidth(), ico.getHeight());
 132             }
 133         } finally {
 134             XToolkit.awtUnlock();
 135         }
 136     }
 137 
 138     private IconInfo getSecurityIconInfo() {
 139         updateIconSize();
 140         return getSecurityIconInfo(currentSize, currentIcon);
 141     }
 142 
 143     XWarningWindow(final Window ownerWindow, long parentWindow, XWindowPeer ownerPeer) {
 144         super(new XCreateWindowParams(new Object[] {
 145                         TARGET, ownerWindow,
 146                         OWNER, Long.valueOf(parentWindow)
 147         }));
 148         this.ownerWindow = ownerWindow;
 149         this.parentWindow = parentWindow;
 150         this.tooltip = new InfoWindow.Tooltip(null, getTarget(),
 151                 new InfoWindow.Tooltip.LiveArguments() {
 152                     public boolean isDisposed() {
 153                         return XWarningWindow.this.isDisposed();
 154                     }
 155                     public Rectangle getBounds() {
 156                         return XWarningWindow.this.getBounds();
 157                     }
 158                     public String getTooltipString() {
 159                         return XWarningWindow.this.ownerWindow.getWarningString();
 160                     }
 161                 });
 162         this.ownerPeer = new WeakReference<XWindowPeer>(ownerPeer);
 163     }
 164 
 165     private void requestNoTaskbar() {
 166         XNETProtocol netProtocol = XWM.getWM().getNETProtocol();
 167         if (netProtocol != null) {
 168             netProtocol.requestState(this, netProtocol.XA_NET_WM_STATE_SKIP_TASKBAR, true);
 169         }
 170     }
 171 
 172     @Override
 173     void postInit(XCreateWindowParams params) {
 174         super.postInit(params);
 175         XToolkit.awtLock();
 176         try {
 177             XWM.setMotifDecor(this, false, 0, 0);
 178             XWM.setOLDecor(this, false, 0);
 179 
 180             long parentWindow = ((Long)params.get(OWNER)).longValue();
 181             XlibWrapper.XSetTransientFor(XToolkit.getDisplay(),
 182                     getWindow(), parentWindow);
 183 
 184             XWMHints hints = getWMHints();
 185             hints.set_flags(hints.get_flags() | (int)XUtilConstants.InputHint | (int)XUtilConstants.StateHint);
 186             hints.set_input(false);
 187             hints.set_initial_state(XUtilConstants.NormalState);
 188             XlibWrapper.XSetWMHints(XToolkit.getDisplay(), getWindow(), hints.pData);
 189 
 190             initWMProtocols();
 191             requestNoTaskbar();
 192         } finally {
 193             XToolkit.awtUnlock();
 194         }
 195     }
 196 
 197     /**
 198      * @param x,y,w,h coordinates of the untrusted window
 199      */
 200     public void reposition(int x, int y, int w, int h) {
 201         Point2D point = AWTAccessor.getWindowAccessor().
 202             calculateSecurityWarningPosition(ownerWindow,
 203                 x, y, w, h);
 204         reshape((int)point.getX(), (int)point.getY(), getWidth(), getHeight());
 205     }
 206 
 207     protected String getWMName() {
 208         return "Warning window";
 209     }
 210 
 211     public Graphics getGraphics() {
 212         if ((surfaceData == null) || (ownerWindow == null)) return null;
 213         return getGraphics(surfaceData,
 214                                  getColor(),
 215                                  getBackground(),
 216                                  getFont());
 217     }
 218     void paint(Graphics g, int x, int y, int width, int height) {
 219         g.drawImage(getSecurityIconInfo().getImage(), 0, 0, null);
 220     }
 221 
 222     String getWarningString() {
 223         return ownerWindow.getWarningString();
 224     }
 225 
 226     int getWidth() {
 227         return getSecurityIconInfo().getWidth();
 228     }
 229 
 230     int getHeight() {
 231         return getSecurityIconInfo().getHeight();
 232     }
 233 
 234     Color getBackground() {
 235         return SystemColor.window;
 236     }
 237     Color getColor() {
 238         return Color.black;
 239     }
 240     Font getFont () {
 241         return ownerWindow.getFont();
 242     }
 243 
 244     @Override
 245     public void repaint() {
 246         final Rectangle bounds = getBounds();
 247         final Graphics g = getGraphics();
 248         if (g != null) {
 249             try {
 250                 paint(g, 0, 0, bounds.width, bounds.height);
 251             } finally {
 252                 g.dispose();
 253             }
 254         }
 255     }
 256     @Override
 257     public void handleExposeEvent(XEvent xev) {
 258         super.handleExposeEvent(xev);
 259 
 260         XExposeEvent xe = xev.get_xexpose();
 261         final int x = scaleDown(xe.get_x());
 262         final int y = scaleDown(xe.get_y());
 263         final int width = scaleDown(xe.get_width());
 264         final int height = scaleDown(xe.get_height());
 265         SunToolkit.executeOnEventHandlerThread(target,
 266                 new Runnable() {
 267                     public void run() {
 268                         final Graphics g = getGraphics();
 269                         if (g != null) {
 270                             try {
 271                                 paint(g, x, y, width, height);
 272                             } finally {
 273                                 g.dispose();
 274                             }
 275                         }
 276                     }
 277                 });
 278     }
 279 
 280     @Override
 281     protected boolean isEventDisabled(XEvent e) {
 282         return true;
 283     }
 284 
 285     /** Send a synthetic UnmapNotify in order to withdraw the window.
 286      */
 287     private void withdraw() {
 288         XEvent req = new XEvent();
 289         try {
 290             long root;
 291             XToolkit.awtLock();
 292             try {
 293                 root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
 294             }
 295             finally {
 296                 XToolkit.awtUnlock();
 297             }
 298 
 299             req.set_type(XConstants.UnmapNotify);
 300 
 301             XUnmapEvent umev = req.get_xunmap();
 302 
 303             umev.set_event(root);
 304             umev.set_window(getWindow());
 305             umev.set_from_configure(false);
 306 
 307             XToolkit.awtLock();
 308             try {
 309                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 310                         root,
 311                         false,
 312                         XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
 313                         req.pData);
 314             }
 315             finally {
 316                 XToolkit.awtUnlock();
 317             }
 318         } finally {
 319             req.dispose();
 320         }
 321     }
 322 
 323     @Override
 324     protected void stateChanged(long time, int oldState, int newState) {
 325         if (newState == XUtilConstants.IconicState) {
 326             super.xSetVisible(false);
 327             withdraw();
 328         }
 329     }
 330 
 331     @Override
 332     protected void setMouseAbove(boolean above) {
 333         super.setMouseAbove(above);
 334         XWindowPeer p = ownerPeer.get();
 335         if (p != null) {
 336             p.updateSecurityWarningVisibility();
 337         }
 338     }
 339 
 340     @Override
 341     protected void enterNotify(long window) {
 342         super.enterNotify(window);
 343         if (window == getWindow()) {
 344             tooltip.enter();
 345         }
 346     }
 347 
 348     @Override
 349     protected void leaveNotify(long window) {
 350         super.leaveNotify(window);
 351         if (window == getWindow()) {
 352             tooltip.exit();
 353         }
 354     }
 355 
 356     @Override
 357     public void xSetVisible(boolean visible) {
 358         super.xSetVisible(visible);
 359 
 360         // The _NET_WM_STATE_SKIP_TASKBAR got reset upon hiding/showing,
 361         // so we request it every time whenever we change the visibility.
 362         requestNoTaskbar();
 363     }
 364 
 365     private final Runnable hidingTask = new Runnable() {
 366         public void run() {
 367             xSetVisible(false);
 368         }
 369     };
 370 
 371     private final Runnable showingTask = new Runnable() {
 372         public void run() {
 373             if (!isVisible()) {
 374                 xSetVisible(true);
 375                 updateIconSize();
 376                 XWindowPeer peer = ownerPeer.get();
 377                 if (peer != null) {
 378                     peer.repositionSecurityWarning();
 379                 }
 380             }
 381             repaint();
 382             if (currentIcon > 0) {
 383                 currentIcon--;
 384                 XToolkit.schedule(showingTask, SHOWING_DELAY);
 385             }
 386         }
 387     };
 388 
 389     public void setSecurityWarningVisible(boolean visible, boolean doSchedule) {
 390         if (visible) {
 391             XToolkit.remove(hidingTask);
 392             XToolkit.remove(showingTask);
 393             if (isVisible()) {
 394                 currentIcon = 0;
 395             } else {
 396                 currentIcon = 3;
 397             }
 398             if (doSchedule) {
 399                 XToolkit.schedule(showingTask, 1);
 400             } else {
 401                 showingTask.run();
 402             }
 403         } else {
 404             XToolkit.remove(showingTask);
 405             XToolkit.remove(hidingTask);
 406             if (!isVisible()) {
 407                 return;
 408             }
 409             if (doSchedule) {
 410                 XToolkit.schedule(hidingTask, HIDING_DELAY);
 411             } else {
 412                 hidingTask.run();
 413             }
 414         }
 415     }
 416 }