diff --git a/src/macosx/native/sun/awt/CGraphicsDevice.m b/src/macosx/native/sun/awt/CGraphicsDevice.m index e20792a231db5d26cc66239dfaaed3b1b4220b13..c04a6a3807f91fcdf59470a25b798bedbd568220 100644 --- a/src/macosx/native/sun/awt/CGraphicsDevice.m +++ b/src/macosx/native/sun/awt/CGraphicsDevice.m @@ -57,7 +57,7 @@ static CFMutableArrayRef getAllValidDisplayModes(jint displayID){ CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); CFIndex numModes = CFArrayGetCount(allModes); - CFMutableArrayRef validModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes + 1, NULL); + CFMutableArrayRef validModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes + 1, &kCFTypeArrayCallBacks); CFIndex n; for (n=0; n < numModes; n++) { diff --git a/test/java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java b/test/java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5bb69314b25bd046af3890e94e59e8bc606b136d --- /dev/null +++ b/test/java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java @@ -0,0 +1,480 @@ +/* + * 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 +*/ + +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; + } +}