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 37 import java.awt.AWTException; 38 import java.awt.Color; 39 import java.awt.DisplayMode; 40 import java.awt.Frame; 41 import java.awt.Graphics; 42 import java.awt.Graphics2D; 43 import java.awt.GraphicsDevice; 44 import java.awt.GraphicsEnvironment; 45 import java.awt.Image; 46 import java.awt.RenderingHints; 47 import java.awt.Robot; 48 import java.awt.event.KeyAdapter; 49 import java.awt.event.KeyEvent; 50 import java.awt.event.MouseAdapter; 51 import java.awt.event.MouseEvent; 52 import java.awt.image.BufferStrategy; 53 import java.awt.image.BufferedImage; 54 import java.awt.image.VolatileImage; 55 import java.util.Random; 56 import 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 63 public 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 }