1/*
2 * Copyright (c) 2005, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 @test
26 @key headful
27 @bug 6275887 6429971 6459792
28 @summary Test that we don't crash when alt+tabbing in and out of
29         fullscreen app
30 @author Dmitri.Trembovetski@sun.com: area=FullScreen
31 @run main/othervm/timeout=100  AltTabCrashTest -auto -changedm
32 @run main/othervm/timeout=100 -Dsun.java2d.d3d=True AltTabCrashTest -auto -changedm
33 @run main/othervm/timeout=100 -Dsun.java2d.d3d=True AltTabCrashTest -auto -usebs -changedm
34 @run main/othervm/timeout=100 -Dsun.java2d.opengl=True AltTabCrashTest -auto
35*/
36
37import java.awt.AWTException;
38import java.awt.Color;
39import java.awt.DisplayMode;
40import java.awt.Frame;
41import java.awt.Graphics;
42import java.awt.Graphics2D;
43import java.awt.GraphicsDevice;
44import java.awt.GraphicsEnvironment;
45import java.awt.Image;
46import java.awt.RenderingHints;
47import java.awt.Robot;
48import java.awt.event.KeyAdapter;
49import java.awt.event.KeyEvent;
50import java.awt.event.MouseAdapter;
51import java.awt.event.MouseEvent;
52import java.awt.image.BufferStrategy;
53import java.awt.image.BufferedImage;
54import java.awt.image.VolatileImage;
55import java.util.Random;
56import java.util.Vector;
57
58/**
59 * Note that the alt+tabbing in and out part will most likely only work
60 * on Windows, and only if there are no interventions.
61 */
62
63public class AltTabCrashTest extends Frame {
64    public static int width;
65    public static int height;
66    public static volatile boolean autoMode;
67    public static boolean useBS;
68    public static final int NUM_OF_BALLS = 70;
69    // number of times to alt+tab in and out of the app
70    public static int altTabs = 5;
71    private final Vector<Ball> balls = new Vector<>();
72    GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()
73        .getDefaultScreenDevice();
74    VolatileImage vimg = null;
75    BufferStrategy bufferStrategy = null;
76    volatile boolean timeToQuit = false;
77    static final Object lock = new Object();
78
79    enum SpriteType {
80        OVALS, VIMAGES, BIMAGES, AAOVALS, TEXT
81    }
82
83    private static boolean changeDM = false;
84    private static SpriteType spriteType;
85    static Random rnd = new Random();
86
87    public AltTabCrashTest( ) {
88        addKeyListener(new KeyAdapter() {
89            public void keyPressed(KeyEvent e) {
90                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
91                    timeToQuit = true;
92                }
93            }
94        });
95        setIgnoreRepaint(true);
96        addMouseListener(new MouseHandler());
97        for (int i = 0; i < NUM_OF_BALLS; i++) {
98            int x = 50 + rnd.nextInt(550), y = 50 + rnd.nextInt(400);
99
100            balls.addElement(createRandomBall(y, x));
101        }
102        setUndecorated(true);
103        gd.setFullScreenWindow(this);
104        GraphicsDevice gd = getGraphicsConfiguration().getDevice();
105        if (gd.isDisplayChangeSupported() && changeDM) {
106            DisplayMode dm = findDisplayMode();
107            if (dm != null) {
108                try {
109                    gd.setDisplayMode(dm);
110                } catch (IllegalArgumentException iae) {
111                    System.err.println("Error setting display mode");
112                }
113            }
114        }
115        if (useBS) {
116            createBufferStrategy(2);
117            bufferStrategy = getBufferStrategy();
118        } else {
119            Graphics2D g = (Graphics2D) getGraphics();
120            render(g);
121            g.dispose();
122        }
123        Thread t = new BallThread();
124        t.start();
125        if (autoMode) {
126            Thread tt = new AltTabberThread();
127            tt.start();
128            synchronized (lock) {
129                while (!timeToQuit) {
130                    try {
131                        lock.wait(200);
132                    } catch (InterruptedException ex) {
133                        ex.printStackTrace();
134                    }
135                }
136            }
137            t = null;
138            dispose();
139        }
140    }
141
142    private Ball createRandomBall(final int y, final int x) {
143        Ball b;
144        SpriteType type;
145
146        if (spriteType == null) {
147            int index = rnd.nextInt(SpriteType.values().length);
148            type = SpriteType.values()[index];
149        } else {
150            type = spriteType;
151        }
152        switch (type) {
153            case VIMAGES: b = new VISpriteBall(x, y); break;
154            case AAOVALS: b = new AAOvalBall(x, y); break;
155            case BIMAGES: b = new BISpriteBall(x, y); break;
156            case TEXT: b = new TextBall(x,y, "Text Sprite!"); break;
157            default: b = new Ball(x, y); break;
158        }
159        return b;
160    }
161
162    private class MouseHandler extends MouseAdapter  {
163        public void mousePressed(MouseEvent e) {
164            synchronized (balls) {
165                balls.addElement(createRandomBall(e.getX(), e.getY()));
166            }
167        }
168    }
169
170    private class AltTabberThread extends Thread {
171        Robot robot;
172
173        void pressAltTab() {
174            robot.keyPress(KeyEvent.VK_ALT);
175            robot.keyPress(KeyEvent.VK_TAB);
176            robot.keyRelease(KeyEvent.VK_TAB);
177            robot.keyRelease(KeyEvent.VK_ALT);
178        }
179        void pressShiftAltTab() {
180            robot.keyPress(KeyEvent.VK_SHIFT);
181            pressAltTab();
182            robot.keyRelease(KeyEvent.VK_SHIFT);
183        }
184        public void run() {
185            try {
186                robot = new Robot();
187                robot.setAutoDelay(200);
188            } catch (AWTException e) {
189                throw new RuntimeException("Can't create robot");
190            }
191            boolean out = true;
192            while (altTabs-- > 0 && !timeToQuit) {
193                System.err.println("Alt+tabber Iteration: "+altTabs);
194                try { Thread.sleep(2500); } catch (InterruptedException ex) {}
195
196                if (out) {
197                    System.err.println("Issuing alt+tab");
198                    pressAltTab();
199                } else {
200                    System.err.println("Issuing shift ");
201                    pressShiftAltTab();
202                }
203                out = !out;
204            }
205            System.err.println("Alt+tabber finished.");
206            synchronized (lock) {
207                timeToQuit = true;
208                lock.notify();
209            }
210        }
211    }
212
213    private class BallThread extends Thread {
214        public void run() {
215            while (!timeToQuit) {
216                if (useBS) {
217                    renderToBS();
218                    bufferStrategy.show();
219                } else {
220                    Graphics g = AltTabCrashTest.this.getGraphics();
221                    render(g);
222                    g.dispose();
223                }
224            }
225            gd.setFullScreenWindow(null);
226            AltTabCrashTest.this.dispose();
227        }
228    }
229
230    static class Ball {
231
232        int x, y;     // current location
233        int dx, dy;   // motion delta
234        int diameter = 40;
235        Color color = Color.red;
236
237        public Ball() {
238        }
239
240        public Ball(int x, int y) {
241            this.x = x;
242            this.y = y;
243            dx = x % 20 + 1;
244            dy = y % 20 + 1;
245            color = new Color(rnd.nextInt(0x00ffffff));
246        }
247
248        public void move() {
249            if (x < 10 || x >= AltTabCrashTest.width - 20)
250                dx = -dx;
251            if (y < 10 || y > AltTabCrashTest.height - 20)
252                dy = -dy;
253            x += dx;
254            y += dy;
255        }
256
257        public void paint(Graphics g, Color c) {
258            if (c == null) {
259                g.setColor(color);
260            } else {
261                g.setColor(c);
262            }
263            g.fillOval(x, y, diameter, diameter);
264        }
265
266    }
267
268    static class TextBall extends Ball {
269        String text;
270        public TextBall(int x, int y, String text) {
271            super(x, y);
272            this.text = text;
273        }
274
275        public void paint(Graphics g, Color c) {
276            if (c == null) {
277                g.setColor(color);
278            } else {
279                g.setColor(c);
280            }
281            g.drawString(text, x, y);
282        }
283    }
284
285    static class AAOvalBall extends Ball {
286        public AAOvalBall(int x, int y) {
287            super(x, y);
288        }
289        public void paint(Graphics g, Color c) {
290            if (c == null) {
291                Graphics2D g2d = (Graphics2D)g.create();
292                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
293                                     RenderingHints.VALUE_ANTIALIAS_ON);
294                g2d.setColor(color);
295                g2d.fillOval(x, y, diameter, diameter);
296            } else {
297                g.setColor(c);
298                g.fillOval(x-2, y-2, diameter+4, diameter+4);
299            }
300        }
301    }
302
303    static abstract class SpriteBall extends Ball {
304        Image image;
305        public SpriteBall(int x, int y) {
306            super(x, y);
307            image = createSprite();
308            Graphics g = image.getGraphics();
309            g.setColor(color);
310            g.fillRect(0, 0, image.getWidth(null), image.getHeight(null));
311        }
312        public void paint(Graphics g, Color c) {
313            if (c != null) {
314                g.setColor(c);
315                g.fillRect(x, y, image.getWidth(null), image.getHeight(null));
316            } else do {
317                validateSprite();
318                g.drawImage(image, x, y, null);
319            } while (renderingIncomplete());
320        }
321        public abstract Image createSprite();
322        public void validateSprite() {}
323        public boolean renderingIncomplete() { return false; }
324    }
325    class VISpriteBall extends SpriteBall {
326
327        public VISpriteBall(int x, int y) {
328            super(x, y);
329        }
330        public boolean renderingIncomplete() {
331            return ((VolatileImage)image).contentsLost();
332        }
333
334        public Image createSprite() {
335            return gd.getDefaultConfiguration().
336                createCompatibleVolatileImage(20, 20);
337        }
338        public void validateSprite() {
339            int result =
340                ((VolatileImage)image).validate(getGraphicsConfiguration());
341            if (result == VolatileImage.IMAGE_INCOMPATIBLE) {
342                image = createSprite();
343                result = VolatileImage.IMAGE_RESTORED;
344            }
345            if (result == VolatileImage.IMAGE_RESTORED) {
346                Graphics g = image.getGraphics();
347                g.setColor(color);
348                g.fillRect(0, 0, image.getWidth(null), image.getHeight(null));
349            }
350        }
351    }
352    class BISpriteBall extends SpriteBall {
353        public BISpriteBall(int x, int y) {
354            super(x, y);
355        }
356        public Image createSprite() {
357            return new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
358        }
359    }
360
361
362    public void renderOffscreen() {
363        Graphics2D g2d = (Graphics2D) vimg.getGraphics();
364        synchronized (balls) {
365            for (Ball b : balls) {
366                b.paint(g2d, getBackground());
367                b.move();
368                b.paint(g2d, null);
369            }
370        }
371        g2d.dispose();
372    }
373
374    public void renderToBS() {
375        width = getWidth();
376        height = getHeight();
377
378        do {
379            Graphics2D g2d = (Graphics2D)bufferStrategy.getDrawGraphics();
380
381            g2d.clearRect(0, 0, width, height);
382            synchronized (balls) {
383                for (Ball b : balls) {
384                    b.move();
385                    b.paint(g2d, null);
386                }
387            }
388            g2d.dispose();
389        } while (bufferStrategy.contentsLost() ||
390                bufferStrategy.contentsRestored());
391    }
392
393    public void render(Graphics g)  {
394        do {
395            height = getBounds().height;
396            width = getBounds().width;
397            if (vimg == null) {
398                vimg = createVolatileImage(width, height);
399                renderOffscreen();
400            }
401            int returnCode = vimg.validate(getGraphicsConfiguration());
402            if (returnCode == VolatileImage.IMAGE_RESTORED) {
403                renderOffscreen();
404            } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) {
405                vimg = getGraphicsConfiguration().
406                    createCompatibleVolatileImage(width, height);
407                renderOffscreen();
408            } else if (returnCode == VolatileImage.IMAGE_OK) {
409                renderOffscreen();
410            }
411            g.drawImage(vimg, 0, 0, this);
412        } while (vimg.contentsLost());
413    }
414
415    public static void main(String args[])  {
416        for (String arg : args) {
417            if (arg.equalsIgnoreCase("-auto")) {
418                autoMode = true;
419                System.err.println("Running in automatic mode using Robot");
420            } else if (arg.equalsIgnoreCase("-usebs")) {
421                useBS = true;
422                System.err.println("Using BufferStrategy instead of VI");
423            } else if (arg.equalsIgnoreCase("-changedm")) {
424                changeDM= true;
425                System.err.println("The test will change display mode");
426            } else if (arg.equalsIgnoreCase("-vi")) {
427                spriteType = SpriteType.VIMAGES;
428            } else if (arg.equalsIgnoreCase("-bi")) {
429                spriteType = SpriteType.BIMAGES;
430            } else if (arg.equalsIgnoreCase("-ov")) {
431                spriteType = SpriteType.OVALS;
432            } else if (arg.equalsIgnoreCase("-aaov")) {
433                spriteType = SpriteType.AAOVALS;
434            } else if (arg.equalsIgnoreCase("-tx")) {
435                spriteType = SpriteType.TEXT;
436            } else {
437                System.err.println("Usage: AltTabCrashTest [-usebs][-auto]" +
438                                   "[-changedm][-vi|-bi|-ov|-aaov|-tx]");
439                System.err.println(" -usebs: use BufferStrategy instead of VI");
440                System.err.println(" -auto: automatically alt+tab in and out" +
441                                   " of the application ");
442                System.err.println(" -changedm: change display mode");
443                System.err.println(" -(vi|bi|ov|tx|aaov) : use only VI, BI, " +
444                                   "text or [AA] [draw]Oval sprites");
445                System.exit(0);
446            }
447        }
448        if (spriteType != null) {
449            System.err.println("The test will only use "+spriteType+" sprites.");
450        }
451        new AltTabCrashTest();
452    }
453
454    private DisplayMode findDisplayMode() {
455        GraphicsDevice gd = getGraphicsConfiguration().getDevice();
456        DisplayMode dms[] = gd.getDisplayModes();
457        DisplayMode currentDM = gd.getDisplayMode();
458        for (DisplayMode dm : dms) {
459            if (dm.getBitDepth() > 8 &&
460                dm.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
461                dm.getBitDepth() != currentDM.getBitDepth() &&
462                dm.getWidth() == currentDM.getWidth() &&
463                dm.getHeight() == currentDM.getHeight())
464            {
465                // found a mode which has the same dimensions but different
466                // depth
467                return dm;
468            }
469            if (dm.getBitDepth() == DisplayMode.BIT_DEPTH_MULTI &&
470                (dm.getWidth() != currentDM.getWidth() ||
471                 dm.getHeight() != currentDM.getHeight()))
472            {
473                // found a mode which has the same depth but different
474                // dimensions
475                return dm;
476            }
477        }
478
479        return null;
480    }
481}
482