8047061: [macosx] Crash when setting display mode
authorpchelko
Wed, 18 Jun 2014 13:21:59 +0400
changeset 25199 4f8ea6dda40d
parent 25198 430b83338f26
child 25200 360a141ded8f
8047061: [macosx] Crash when setting display mode Reviewed-by: anthony, alexsch
jdk/src/macosx/native/sun/awt/CGraphicsDevice.m
jdk/test/java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java
--- a/jdk/src/macosx/native/sun/awt/CGraphicsDevice.m	Tue Jun 17 19:05:06 2014 +0400
+++ b/jdk/src/macosx/native/sun/awt/CGraphicsDevice.m	Wed Jun 18 13:21:59 2014 +0400
@@ -57,7 +57,7 @@
     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++) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java	Wed Jun 18 13:21:59 2014 +0400
@@ -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<Ball> 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;
+    }
+}