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