XWarningWindow.java revision 13221:bc2d1130105f
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 */
25package sun.awt.X11;
26
27import java.awt.*;
28import java.awt.geom.Point2D;
29import java.lang.ref.WeakReference;
30
31import sun.awt.IconInfo;
32import sun.awt.AWTAccessor;
33import sun.awt.SunToolkit;
34
35class 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}
417