8232200: [macos 10.15] Windows in fullscreen tests jumps around the screen
authorserb
Thu, 24 Oct 2019 14:54:31 -0700
changeset 59175 d2123a27cfe7
parent 59174 f4270450976b
child 59176 f5adbf111424
8232200: [macos 10.15] Windows in fullscreen tests jumps around the screen Reviewed-by: prr
src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
test/jdk/ProblemList.txt
test/jdk/java/awt/GraphicsDevice/DisplayModes/CycleDMImage.java
--- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java	Wed Oct 30 13:47:49 2019 -0700
+++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java	Thu Oct 24 14:54:31 2019 -0700
@@ -33,11 +33,14 @@
 import java.awt.Rectangle;
 import java.awt.Window;
 import java.awt.geom.Rectangle2D;
+import java.awt.peer.WindowPeer;
 import java.util.Objects;
 
 import sun.java2d.SunGraphicsEnvironment;
 import sun.java2d.opengl.CGLGraphicsConfig;
 
+import static java.awt.peer.ComponentPeer.SET_BOUNDS;
+
 public final class CGraphicsDevice extends GraphicsDevice
         implements DisplayChangedListener {
 
@@ -129,6 +132,7 @@
     }
 
     public void invalidate(final int defaultDisplayID) {
+        //TODO do we need to restore the full-screen window/modes on old device?
         displayID = defaultDisplayID;
     }
 
@@ -138,7 +142,8 @@
         yResolution = nativeGetYResolution(displayID);
         bounds = nativeGetBounds(displayID).getBounds(); //does integer rounding
         initScaleFactor();
-        //TODO configs/fullscreenWindow/modes?
+        resizeFSWindow(getFullScreenWindow(), bounds);
+        //TODO configs?
     }
 
     @Override
@@ -217,6 +222,18 @@
         }
     }
 
+    /**
+     * Reapplies the size of this device to the full-screen window.
+     */
+    private static void resizeFSWindow(final Window w, final Rectangle b) {
+        if (w != null) {
+            WindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
+            if (peer != null) {
+                peer.setBounds(b.x, b.y, b.width, b.height, SET_BOUNDS);
+            }
+        }
+    }
+
     @Override
     public boolean isDisplayChangeSupported() {
         return true;
@@ -229,10 +246,7 @@
         }
         if (!Objects.equals(dm, getDisplayMode())) {
             nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(),
-                    dm.getBitDepth(), dm.getRefreshRate());
-            if (isFullScreenSupported() && getFullScreenWindow() != null) {
-                getFullScreenWindow().setSize(dm.getWidth(), dm.getHeight());
-            }
+                                 dm.getBitDepth(), dm.getRefreshRate());
         }
     }
 
--- a/test/jdk/ProblemList.txt	Wed Oct 30 13:47:49 2019 -0700
+++ b/test/jdk/ProblemList.txt	Thu Oct 24 14:54:31 2019 -0700
@@ -530,6 +530,8 @@
 java/awt/TextArea/AutoScrollOnSelectAndAppend/AutoScrollOnSelectAndAppend.java 8213120 macosx-all
 java/awt/Window/MainKeyWindowTest/TestMainKeyWindow.java 8213126 macosx-all
 
+java/awt/GraphicsDevice/DisplayModes/CycleDMImage.java 7099223 linux-all,solaris-all,windows-all
+
 ############################################################################
 
 # jdk_beans
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/awt/GraphicsDevice/DisplayModes/CycleDMImage.java	Thu Oct 24 14:54:31 2019 -0700
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2003, 2019, 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.
+ */
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.DisplayMode;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.Transparency;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+/**
+ * @test
+ * @bug 4836241 6364134 8232200
+ * @key headful
+ * @summary verify that images are restored correctly after display mode
+ *          switches and that no other rendering or crash problems occur
+ * @run main/timeout=500 CycleDMImage
+ */
+public class CycleDMImage extends Component implements Runnable, KeyListener {
+    /**
+     * This test cycles through all available display modes, waiting after
+     * each call to setDisplayMode() to ensure that the new one is active
+     * before proceeding on to the next one.  The Component is filled with
+     * a green background color and then compatible images of all 3
+     * Transparency types are copied to the screen.  The results of these
+     * operations are checked (using Robot) and the test fails if any of the
+     * rendering is wrong in any of the DisplayModes.  The purpose of this
+     * test is to ensure that display mode switches do not cause problems
+     * with image restoration (or other rendering operations).
+     */
+    boolean painted = false;
+    boolean earlyExit = false;
+    Image rImage = null, wImage = null, bImage = null;
+    int imgSize = 10;
+    Robot robot = null;
+    volatile static boolean done = false;
+    static String errorMessage = null;
+
+    public synchronized void paint(Graphics g) {
+        if (!painted) {
+            painted = true;
+            (new Thread(this)).start();
+        }
+        if (rImage == null) {
+            GraphicsConfiguration gc = getGraphicsConfiguration();
+            rImage = gc.createCompatibleImage(imgSize, imgSize);
+            wImage = gc.createCompatibleImage(imgSize, imgSize,
+                                              Transparency.BITMASK);
+            bImage = gc.createCompatibleImage(imgSize, imgSize,
+                                              Transparency.TRANSLUCENT);
+            Graphics imgGraphics = rImage.getGraphics();
+            imgGraphics.setColor(Color.red);
+            imgGraphics.fillRect(0, 0, imgSize, imgSize);
+            imgGraphics = wImage.getGraphics();
+            imgGraphics.setColor(Color.white);
+            imgGraphics.fillRect(0, 0, imgSize, imgSize);
+            imgGraphics = bImage.getGraphics();
+            imgGraphics.setColor(Color.blue);
+            imgGraphics.fillRect(0, 0, imgSize, imgSize);
+        }
+        g.setColor(Color.green);
+        g.fillRect(0, 0, getWidth(), getHeight());
+        g.drawImage(rImage, 0, 0, this);
+        g.drawImage(wImage, imgSize, 0, this);
+        g.drawImage(bImage, imgSize*2, 0, this);
+        g.drawImage(rImage, 0, getHeight()-imgSize, null);
+        g.drawImage(rImage, getWidth()-imgSize, getHeight()-imgSize, null);
+        g.drawImage(rImage, getWidth()-imgSize, 0, null);
+    }
+
+    static void delay(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (Exception e) {}
+    }
+
+    public boolean checkResult(DisplayMode dm) {
+        if (robot == null) {
+            try {
+                robot = new Robot();
+            }
+            catch (Exception e) {
+                errorMessage = "Problems creating Robot";
+                return false;
+            }
+        }
+        Rectangle bounds = getGraphicsConfiguration().getBounds();
+        int pixels[] = new int[imgSize * 4];
+        BufferedImage clientPixels =
+            robot.createScreenCapture(new Rectangle(bounds.x, bounds.y,
+                                                    imgSize*4, 1));
+        clientPixels.getRGB(0, 0, imgSize * 4, 1, pixels, 0, getWidth());
+        // Now check the results.  We expect: imgSize blocks of r/w/b/g
+        int colors[] = {0xffff0000, 0xffffffff, 0xff0000ff, 0xff00ff00};
+        for (int color = 0; color < 4; ++color) {
+            for (int i = 0; i < imgSize; ++i) {
+                int pixelIndex = imgSize * color + i;
+                if (pixels[pixelIndex] != colors[color]) {
+                    errorMessage = "\n    DisplayMode(" +
+                        dm.getWidth() + " x " +
+                        dm.getHeight() + " x " +
+                        dm.getBitDepth() + "bpp x " +
+                        dm.getRefreshRate() +
+                        ")\n    Pixel " + i +
+                        ": Expected " +
+                        Integer.toHexString(colors[color]) +
+                        ", got " +
+                        Integer.toHexString(pixels[pixelIndex]) +
+                        " at " + i;
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    boolean displayModesEqual(DisplayMode dm1, DisplayMode dm2) {
+        if (dm1.equals(dm2)) {
+            return true;
+        }
+        // not enough - check whether the modes are equal except for
+        // refreshRate, if either mode has REFRESH_RATE_UNKNOWN
+        // value for this parameter
+        if (dm1.getWidth() != dm2.getWidth() ||
+            dm1.getHeight() != dm2.getHeight() ||
+            dm1.getBitDepth() != dm2.getBitDepth())
+        {
+            // core parameters must match
+            return false;
+        }
+        // Now we know that w1 == w2, h1 == h2, and d1 == d2; must be the
+        // case that the refresh rates do not match.
+        // Is either one REFRESH_RATE_UNKNOWN?
+        if (dm1.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN ||
+            dm2.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN)
+        {
+            return true;
+        }
+        return false;
+    }
+
+    public void run() {
+        GraphicsDevice gd = getGraphicsConfiguration().getDevice();
+        gd.setFullScreenWindow((Window) getParent());
+        // First, delay a bit just to let the fullscreen window
+        // settle down before switching display modes
+        delay(1000);
+
+        if (!gd.isDisplayChangeSupported()) {
+            System.err.println("Display change is not supported,"+
+                               " the test is considered passed.");
+            finished();
+            return;
+        }
+
+        // We are really only interested in unique w/h/d resolutions
+        // and it would be nice to skip the myriad of refresh rate
+        // varations, so let us construct a subset that contains
+        // only those DisplayModes with unique w/h/d values
+        // Also, due to a current bug (4837228), we should skip the
+        // 24-bit depths since attempting to create bitmask-transparent
+        // ddraw images can cause the test to crash (we should change this
+        // test to include that depth when the bug is fixed).
+        ArrayList<DisplayMode> dmSubset = new ArrayList<>();
+        for (final DisplayMode dm : gd.getDisplayModes()) {
+            boolean skip = false;
+            for (final DisplayMode dmUnique : dmSubset) {
+                int bitDepth = dm.getBitDepth();
+                if (bitDepth == 24 ||
+                        (dmUnique.getWidth() == dm.getWidth() &&
+                         dmUnique.getHeight() == dm.getHeight() &&
+                         dmUnique.getBitDepth() == dm.getBitDepth())) {
+                    skip = true;
+                    break;
+                }
+            }
+            if (!skip) {
+                dmSubset.add(dm);
+            }
+        }
+
+        // Now, cycle through the display modes one-by-one.  For
+        // each new display mode, delay until we detect that the
+        // new mode == the current mode.  Then delay an additional
+        // second (to allow any repaints to occur)
+
+        for (DisplayMode newDM : dmSubset) {
+            gd.setDisplayMode(newDM);
+            while (!displayModesEqual(newDM, gd.getDisplayMode())) {
+                delay(100);
+            }
+            // Delay another few seconds after the new display mode is active
+            delay(4000);
+
+            // Check the rendering results
+            if (!checkResult(newDM)) {
+                finished();
+                return;
+            }
+
+            // Escape out if requested by the user
+            if (earlyExit) {
+                System.out.println("Exiting test early, by request");
+                System.exit(0);
+            }
+        }
+
+        // Done with test; if we got here, we passed
+        System.out.println("Passed");
+        finished();
+    }
+
+    public static void finished() {
+        synchronized (CycleDMImage.class) {
+            done = true;
+            CycleDMImage.class.notify();
+        }
+    }
+
+    /**
+     * KeyListener methods; these provide a way for a user to escape out of
+     * a potentially lengthy test.
+     */
+
+    public void keyTyped(KeyEvent e) {
+    }
+
+    public void keyPressed(KeyEvent e) {
+        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+            earlyExit = true;
+        }
+    }
+
+    public void keyReleased(KeyEvent e) {
+    }
+
+    public static void main(String args[]) {
+        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        for (final GraphicsDevice gd: ge.getScreenDevices()) {
+            if (!gd.isFullScreenSupported()) {
+                System.err.println("FullScreen mode is not supported,"+
+                                           " the test is considered passed.");
+                continue;
+            }
+            done = false;
+            Frame frame = new Frame(gd.getDefaultConfiguration());
+            try {
+                frame.setSize(400, 400);
+                frame.setUndecorated(true);
+                CycleDMImage comp = new CycleDMImage();
+                frame.addKeyListener(comp);
+                frame.add(comp);
+                frame.setVisible(true);
+                // Sleep awaiting frame disposal
+                synchronized (CycleDMImage.class) {
+                    while (!done) {
+                        try {
+                            CycleDMImage.class.wait(100);
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                }
+            } finally {
+                frame.dispose();
+            }
+            if (errorMessage != null) {
+                throw new RuntimeException(errorMessage);
+            }
+            // delay a bit just to let the fullscreen window disposing complete
+            // before switching to next display
+            delay(4000);
+        }
+    }
+}