/* * Copyright (c) 2005, 2014 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* @test @bug 6275887 6429971 6459792 @summary Test that we don't crash when alt+tabbing in and out of fullscreen app @author Dmitri.Trembovetski@sun.com: area=FullScreen @run main/othervm/timeout=100 AltTabCrashTest -auto -changedm @run main/othervm/timeout=100 -Dsun.java2d.d3d=True AltTabCrashTest -auto -changedm @run main/othervm/timeout=100 -Dsun.java2d.d3d=True AltTabCrashTest -auto -usebs -changedm @run main/othervm/timeout=100 -Dsun.java2d.opengl=True AltTabCrashTest -auto @key randomness */ import java.awt.AWTException; import java.awt.Color; import java.awt.DisplayMode; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Robot; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.util.Random; import java.util.Vector; /** * Note that the alt+tabbing in and out part will most likely only work * on Windows, and only if there are no interventions. */ public class AltTabCrashTest extends Frame { public static int width; public static int height; public static volatile boolean autoMode; public static boolean useBS; public static final int NUM_OF_BALLS = 70; // number of times to alt+tab in and out of the app public static int altTabs = 5; private final Vector balls = new Vector<>(); GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice(); VolatileImage vimg = null; BufferStrategy bufferStrategy = null; volatile boolean timeToQuit = false; static final Object lock = new Object(); enum SpriteType { OVALS, VIMAGES, BIMAGES, AAOVALS, TEXT } private static boolean changeDM = false; private static SpriteType spriteType; static Random rnd = new Random(); public AltTabCrashTest( ) { addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { timeToQuit = true; } } }); setIgnoreRepaint(true); addMouseListener(new MouseHandler()); for (int i = 0; i < NUM_OF_BALLS; i++) { int x = 50 + rnd.nextInt(550), y = 50 + rnd.nextInt(400); balls.addElement(createRandomBall(y, x)); } setUndecorated(true); gd.setFullScreenWindow(this); GraphicsDevice gd = getGraphicsConfiguration().getDevice(); if (gd.isDisplayChangeSupported() && changeDM) { DisplayMode dm = findDisplayMode(); if (dm != null) { try { gd.setDisplayMode(dm); } catch (IllegalArgumentException iae) { System.err.println("Error setting display mode"); } } } if (useBS) { createBufferStrategy(2); bufferStrategy = getBufferStrategy(); } else { Graphics2D g = (Graphics2D) getGraphics(); render(g); g.dispose(); } Thread t = new BallThread(); t.start(); if (autoMode) { Thread tt = new AltTabberThread(); tt.start(); synchronized (lock) { while (!timeToQuit) { try { lock.wait(200); } catch (InterruptedException ex) { ex.printStackTrace(); } } } t = null; dispose(); } } private Ball createRandomBall(final int y, final int x) { Ball b; SpriteType type; if (spriteType == null) { int index = rnd.nextInt(SpriteType.values().length); type = SpriteType.values()[index]; } else { type = spriteType; } switch (type) { case VIMAGES: b = new VISpriteBall(x, y); break; case AAOVALS: b = new AAOvalBall(x, y); break; case BIMAGES: b = new BISpriteBall(x, y); break; case TEXT: b = new TextBall(x,y, "Text Sprite!"); break; default: b = new Ball(x, y); break; } return b; } private class MouseHandler extends MouseAdapter { public void mousePressed(MouseEvent e) { synchronized (balls) { balls.addElement(createRandomBall(e.getX(), e.getY())); } } } private class AltTabberThread extends Thread { Robot robot; void pressAltTab() { robot.keyPress(KeyEvent.VK_ALT); robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_ALT); } void pressShiftAltTab() { robot.keyPress(KeyEvent.VK_SHIFT); pressAltTab(); robot.keyRelease(KeyEvent.VK_SHIFT); } public void run() { try { robot = new Robot(); robot.setAutoDelay(200); } catch (AWTException e) { throw new RuntimeException("Can't create robot"); } boolean out = true; while (altTabs-- > 0 && !timeToQuit) { System.err.println("Alt+tabber Iteration: "+altTabs); try { Thread.sleep(2500); } catch (InterruptedException ex) {} if (out) { System.err.println("Issuing alt+tab"); pressAltTab(); } else { System.err.println("Issuing shift "); pressShiftAltTab(); } out = !out; } System.err.println("Alt+tabber finished."); synchronized (lock) { timeToQuit = true; lock.notify(); } } } private class BallThread extends Thread { public void run() { while (!timeToQuit) { if (useBS) { renderToBS(); bufferStrategy.show(); } else { Graphics g = AltTabCrashTest.this.getGraphics(); render(g); g.dispose(); } } gd.setFullScreenWindow(null); AltTabCrashTest.this.dispose(); } } static class Ball { int x, y; // current location int dx, dy; // motion delta int diameter = 40; Color color = Color.red; public Ball() { } public Ball(int x, int y) { this.x = x; this.y = y; dx = x % 20 + 1; dy = y % 20 + 1; color = new Color(rnd.nextInt(0x00ffffff)); } public void move() { if (x < 10 || x >= AltTabCrashTest.width - 20) dx = -dx; if (y < 10 || y > AltTabCrashTest.height - 20) dy = -dy; x += dx; y += dy; } public void paint(Graphics g, Color c) { if (c == null) { g.setColor(color); } else { g.setColor(c); } g.fillOval(x, y, diameter, diameter); } } static class TextBall extends Ball { String text; public TextBall(int x, int y, String text) { super(x, y); this.text = text; } public void paint(Graphics g, Color c) { if (c == null) { g.setColor(color); } else { g.setColor(c); } g.drawString(text, x, y); } } static class AAOvalBall extends Ball { public AAOvalBall(int x, int y) { super(x, y); } public void paint(Graphics g, Color c) { if (c == null) { Graphics2D g2d = (Graphics2D)g.create(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(color); g2d.fillOval(x, y, diameter, diameter); } else { g.setColor(c); g.fillOval(x-2, y-2, diameter+4, diameter+4); } } } static abstract class SpriteBall extends Ball { Image image; public SpriteBall(int x, int y) { super(x, y); image = createSprite(); Graphics g = image.getGraphics(); g.setColor(color); g.fillRect(0, 0, image.getWidth(null), image.getHeight(null)); } public void paint(Graphics g, Color c) { if (c != null) { g.setColor(c); g.fillRect(x, y, image.getWidth(null), image.getHeight(null)); } else do { validateSprite(); g.drawImage(image, x, y, null); } while (renderingIncomplete()); } public abstract Image createSprite(); public void validateSprite() {} public boolean renderingIncomplete() { return false; } } class VISpriteBall extends SpriteBall { public VISpriteBall(int x, int y) { super(x, y); } public boolean renderingIncomplete() { return ((VolatileImage)image).contentsLost(); } public Image createSprite() { return gd.getDefaultConfiguration(). createCompatibleVolatileImage(20, 20); } public void validateSprite() { int result = ((VolatileImage)image).validate(getGraphicsConfiguration()); if (result == VolatileImage.IMAGE_INCOMPATIBLE) { image = createSprite(); result = VolatileImage.IMAGE_RESTORED; } if (result == VolatileImage.IMAGE_RESTORED) { Graphics g = image.getGraphics(); g.setColor(color); g.fillRect(0, 0, image.getWidth(null), image.getHeight(null)); } } } class BISpriteBall extends SpriteBall { public BISpriteBall(int x, int y) { super(x, y); } public Image createSprite() { return new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB); } } public void renderOffscreen() { Graphics2D g2d = (Graphics2D) vimg.getGraphics(); synchronized (balls) { for (Ball b : balls) { b.paint(g2d, getBackground()); b.move(); b.paint(g2d, null); } } g2d.dispose(); } public void renderToBS() { width = getWidth(); height = getHeight(); do { Graphics2D g2d = (Graphics2D)bufferStrategy.getDrawGraphics(); g2d.clearRect(0, 0, width, height); synchronized (balls) { for (Ball b : balls) { b.move(); b.paint(g2d, null); } } g2d.dispose(); } while (bufferStrategy.contentsLost() || bufferStrategy.contentsRestored()); } public void render(Graphics g) { do { height = getBounds().height; width = getBounds().width; if (vimg == null) { vimg = createVolatileImage(width, height); renderOffscreen(); } int returnCode = vimg.validate(getGraphicsConfiguration()); if (returnCode == VolatileImage.IMAGE_RESTORED) { renderOffscreen(); } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) { vimg = getGraphicsConfiguration(). createCompatibleVolatileImage(width, height); renderOffscreen(); } else if (returnCode == VolatileImage.IMAGE_OK) { renderOffscreen(); } g.drawImage(vimg, 0, 0, this); } while (vimg.contentsLost()); } public static void main(String args[]) { for (String arg : args) { if (arg.equalsIgnoreCase("-auto")) { autoMode = true; System.err.println("Running in automatic mode using Robot"); } else if (arg.equalsIgnoreCase("-usebs")) { useBS = true; System.err.println("Using BufferStrategy instead of VI"); } else if (arg.equalsIgnoreCase("-changedm")) { changeDM= true; System.err.println("The test will change display mode"); } else if (arg.equalsIgnoreCase("-vi")) { spriteType = SpriteType.VIMAGES; } else if (arg.equalsIgnoreCase("-bi")) { spriteType = SpriteType.BIMAGES; } else if (arg.equalsIgnoreCase("-ov")) { spriteType = SpriteType.OVALS; } else if (arg.equalsIgnoreCase("-aaov")) { spriteType = SpriteType.AAOVALS; } else if (arg.equalsIgnoreCase("-tx")) { spriteType = SpriteType.TEXT; } else { System.err.println("Usage: AltTabCrashTest [-usebs][-auto]" + "[-changedm][-vi|-bi|-ov|-aaov|-tx]"); System.err.println(" -usebs: use BufferStrategy instead of VI"); System.err.println(" -auto: automatically alt+tab in and out" + " of the application "); System.err.println(" -changedm: change display mode"); System.err.println(" -(vi|bi|ov|tx|aaov) : use only VI, BI, " + "text or [AA] [draw]Oval sprites"); System.exit(0); } } if (spriteType != null) { System.err.println("The test will only use "+spriteType+" sprites."); } new AltTabCrashTest(); } private DisplayMode findDisplayMode() { GraphicsDevice gd = getGraphicsConfiguration().getDevice(); DisplayMode dms[] = gd.getDisplayModes(); DisplayMode currentDM = gd.getDisplayMode(); for (DisplayMode dm : dms) { if (dm.getBitDepth() > 8 && dm.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI && dm.getBitDepth() != currentDM.getBitDepth() && dm.getWidth() == currentDM.getWidth() && dm.getHeight() == currentDM.getHeight()) { // found a mode which has the same dimensions but different // depth return dm; } if (dm.getBitDepth() == DisplayMode.BIT_DEPTH_MULTI && (dm.getWidth() != currentDM.getWidth() || dm.getHeight() != currentDM.getHeight())) { // found a mode which has the same depth but different // dimensions return dm; } } return null; } }