8003173: [macosx] Fullscreen on Mac leaves an empty rectangle
authorserb
Thu, 24 Jan 2013 17:50:03 +0400
changeset 15331 410070becfef
parent 15330 1d23649a5422
child 15332 aa02ac4b20b7
8003173: [macosx] Fullscreen on Mac leaves an empty rectangle Reviewed-by: anthony, alexsch
jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java
jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
jdk/test/java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java
--- a/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java	Thu Jan 24 17:26:32 2013 +0400
+++ b/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java	Thu Jan 24 17:50:03 2013 +0400
@@ -30,6 +30,7 @@
 import java.awt.Window;
 import java.awt.AWTPermission;
 import java.awt.DisplayMode;
+import java.util.Objects;
 
 import sun.java2d.opengl.CGLGraphicsConfig;
 
@@ -122,12 +123,12 @@
         boolean fsSupported = isFullScreenSupported();
 
         if (fsSupported && old != null) {
-            // enter windowed mode (and restore original display mode)
-            exitFullScreenExclusive(old);
+            // restore original display mode and enter windowed mode.
             if (originalMode != null) {
                 setDisplayMode(originalMode);
                 originalMode = null;
             }
+            exitFullScreenExclusive(old);
         }
 
         super.setFullScreenWindow(w);
@@ -186,13 +187,20 @@
     }
 
     @Override
-    public void setDisplayMode(DisplayMode dm) {
+    public void setDisplayMode(final DisplayMode dm) {
         if (dm == null) {
             throw new IllegalArgumentException("Invalid display mode");
         }
-        nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(), dm.getBitDepth(), dm.getRefreshRate());
-        if (isFullScreenSupported() && getFullScreenWindow() != null) {
-            getFullScreenWindow().setSize(dm.getWidth(), dm.getHeight());
+        if (!Objects.equals(dm, getDisplayMode())) {
+            final Window w = getFullScreenWindow();
+            if (w != null) {
+                exitFullScreenExclusive(w);
+            }
+            nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(),
+                                 dm.getBitDepth(), dm.getRefreshRate());
+            if (isFullScreenSupported() && w != null) {
+                enterFullScreenExclusive(w);
+            }
         }
     }
 
--- a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java	Thu Jan 24 17:26:32 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java	Thu Jan 24 17:50:03 2013 +0400
@@ -53,7 +53,7 @@
 
     private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer");
 
-    private PlatformWindow platformWindow;
+    private final PlatformWindow platformWindow;
 
     // Window bounds reported by the native system (as opposed to
     // regular bounds inherited from LWComponentPeer which are
@@ -554,12 +554,14 @@
 
     /**
      * Called by the {@code PlatformWindow} when this window is moved/resized by
-     * user. There's no notifyReshape() in LWComponentPeer as the only
-     * components which could be resized by user are top-level windows.
+     * user or window insets are changed. There's no notifyReshape() in
+     * LWComponentPeer as the only components which could be resized by user are
+     * top-level windows.
      */
     public final void notifyReshape(int x, int y, int w, int h) {
         final boolean moved;
         final boolean resized;
+        final boolean invalid = updateInsets(platformWindow.getInsets());
         synchronized (getStateLock()) {
             moved = (x != sysX) || (y != sysY);
             resized = (w != sysW) || (h != sysH);
@@ -570,7 +572,7 @@
         }
 
         // Check if anything changed
-        if (!moved && !resized) {
+        if (!moved && !resized && !invalid) {
             return;
         }
         // First, update peer's bounds
@@ -584,10 +586,10 @@
         }
 
         // Third, COMPONENT_MOVED/COMPONENT_RESIZED/PAINT events
-        if (moved) {
+        if (moved || invalid) {
             handleMove(x, y, true);
         }
-        if (resized) {
+        if (resized || invalid) {
             handleResize(w, h, true);
             repaintPeer();
         }
@@ -999,27 +1001,21 @@
         }
     }
 
-    /*
-     * Request the window insets from the delegate and compares it
-     * with the current one. This method is mostly called by the
-     * delegate, e.g. when the window state is changed and insets
-     * should be recalculated.
-     *
+    /**
+     * Request the window insets from the delegate and compares it with the
+     * current one. This method is mostly called by the delegate, e.g. when the
+     * window state is changed and insets should be recalculated.
+     * <p/>
      * This method may be called on the toolkit thread.
      */
-    public boolean updateInsets(Insets newInsets) {
-        boolean changed = false;
+    public final boolean updateInsets(final Insets newInsets) {
         synchronized (getStateLock()) {
-            changed = (insets.equals(newInsets));
+            if (insets.equals(newInsets)) {
+                return false;
+            }
             insets = newInsets;
         }
-
-        if (changed) {
-            replaceSurfaceData();
-            repaintPeer();
-        }
-
-        return changed;
+        return true;
     }
 
     public static LWWindowPeer getWindowUnderCursor() {
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java	Thu Jan 24 17:26:32 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java	Thu Jan 24 17:50:03 2013 +0400
@@ -27,7 +27,6 @@
 
 import java.awt.*;
 import java.awt.geom.Rectangle2D;
-import java.awt.image.VolatileImage;
 
 import sun.awt.CGraphicsConfig;
 import sun.awt.CGraphicsEnvironment;
@@ -89,29 +88,8 @@
         return peer;
     }
 
-    public void enterFullScreenMode(final long nsWindowPtr) {
+    public void enterFullScreenMode() {
         CWrapper.NSView.enterFullScreenMode(ptr);
-
-        // REMIND: CGLSurfaceData expects top-level's size
-        // and therefore we need to account insets before
-        // recreating the surface data
-        Insets insets = peer.getInsets();
-
-        Rectangle screenBounds;
-        final long screenPtr = CWrapper.NSWindow.screen(nsWindowPtr);
-        try {
-            screenBounds = CWrapper.NSScreen.frame(screenPtr).getBounds();
-        } finally {
-            CWrapper.NSObject.release(screenPtr);
-        }
-
-        // the move/size notification from the underlying system comes
-        // but it contains a bounds smaller than the whole screen
-        // and therefore we need to create the synthetic notifications
-        peer.notifyReshape(screenBounds.x - insets.left,
-                           screenBounds.y - insets.bottom,
-                           screenBounds.width + insets.left + insets.right,
-                           screenBounds.height + insets.top + insets.bottom);
     }
 
     public void exitFullScreenMode() {
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Jan 24 17:26:32 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Jan 24 17:50:03 2013 +0400
@@ -38,7 +38,6 @@
 import sun.java2d.SurfaceData;
 import sun.java2d.opengl.CGLSurfaceData;
 import sun.lwawt.*;
-import sun.lwawt.LWWindowPeer.PeerType;
 import sun.util.logging.PlatformLogger;
 
 import com.apple.laf.*;
@@ -196,7 +195,8 @@
     // 1) setting native bounds via nativeSetBounds() call
     // 2) getting notification from the native level via deliverMoveResizeEvent()
     private Rectangle nativeBounds = new Rectangle(0, 0, 0, 0);
-    private volatile boolean isFullScreenMode = false;
+    private volatile boolean isFullScreenMode;
+    private boolean isFullScreenAnimationOn;
 
     private Window target;
     private LWWindowPeer peer;
@@ -414,8 +414,10 @@
 
     @Override // PlatformWindow
     public Insets getInsets() {
-        final Insets insets = nativeGetNSWindowInsets(getNSWindowPtr());
-        return insets;
+        if (!isFullScreenMode) {
+            return nativeGetNSWindowInsets(getNSWindowPtr());
+        }
+        return new Insets(0, 0, 0, 0);
     }
 
     @Override // PlatformWindow
@@ -727,7 +729,19 @@
     @Override
     public void enterFullScreenMode() {
         isFullScreenMode = true;
-        contentView.enterFullScreenMode(getNSWindowPtr());
+        contentView.enterFullScreenMode();
+        // the move/size notification from the underlying system comes
+        // but it contains a bounds smaller than the whole screen
+        // and therefore we need to create the synthetic notifications
+        Rectangle screenBounds;
+        final long screenPtr = CWrapper.NSWindow.screen(getNSWindowPtr());
+        try {
+            screenBounds = CWrapper.NSScreen.frame(screenPtr).getBounds();
+        } finally {
+            CWrapper.NSObject.release(screenPtr);
+        }
+        peer.notifyReshape(screenBounds.x, screenBounds.y, screenBounds.width,
+                           screenBounds.height);
     }
 
     @Override
@@ -874,11 +888,10 @@
         final Rectangle oldB = nativeBounds;
         nativeBounds = new Rectangle(x, y, width, height);
         peer.notifyReshape(x, y, width, height);
-        if (byUser && !oldB.getSize().equals(nativeBounds.getSize())) {
+        if ((byUser && !oldB.getSize().equals(nativeBounds.getSize()))
+            || isFullScreenAnimationOn) {
             flushBuffers();
         }
-        //TODO validateSurface already called from notifyReshape
-        validateSurface();
     }
 
     private void deliverWindowClosingEvent() {
@@ -978,27 +991,19 @@
         orderAboveSiblings();
     }
 
-    private void updateDisplay() {
-        EventQueue.invokeLater(new Runnable() {
-            public void run() {
-                validateSurface();
-            }
-        });
+    private void windowWillEnterFullScreen() {
+        isFullScreenAnimationOn = true;
+    }
+
+    private void windowDidEnterFullScreen() {
+        isFullScreenAnimationOn = false;
     }
 
-    private void updateWindowContent() {
-        ComponentEvent resizeEvent = new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED);
-        SunToolkit.postEvent(SunToolkit.targetToAppContext(target), resizeEvent);
+    private void windowWillExitFullScreen() {
+        isFullScreenAnimationOn = true;
     }
 
-    private void windowWillEnterFullScreen() {
-        updateWindowContent();
-    }
-    private void windowDidEnterFullScreen() {
-        updateDisplay();
+    private void windowDidExitFullScreen() {
+        isFullScreenAnimationOn = false;
     }
-    private void windowWillExitFullScreen() {
-        updateWindowContent();
-    }
-    private void windowDidExitFullScreen() {}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java	Thu Jan 24 17:50:03 2013 +0400
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2013, 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.AWTException;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.DisplayMode;
+import java.awt.Frame;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
+import java.awt.Robot;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.image.BufferedImage;
+
+import sun.awt.SunToolkit;
+
+/**
+ * @test
+ * @bug 8003173 7019055
+ * @summary Full-screen windows should have the proper insets.
+ * @author Sergey Bylokhov
+ */
+public final class FullScreenInsets {
+
+    private static boolean passed = true;
+
+    public static void main(final String[] args) {
+        final GraphicsEnvironment ge = GraphicsEnvironment
+                .getLocalGraphicsEnvironment();
+        final GraphicsDevice[] devices = ge.getScreenDevices();
+
+        final Window wGreen = new Frame();
+        wGreen.setBackground(Color.GREEN);
+        wGreen.setSize(300, 300);
+        wGreen.setVisible(true);
+        sleep();
+        final Insets iGreen = wGreen.getInsets();
+        final Dimension sGreen = wGreen.getSize();
+
+        final Window wRed = new Frame();
+        wRed.setBackground(Color.RED);
+        wRed.setSize(300, 300);
+        wRed.setVisible(true);
+        sleep();
+        final Insets iRed = wGreen.getInsets();
+        final Dimension sRed = wGreen.getSize();
+
+        for (final GraphicsDevice device : devices) {
+            if (!device.isFullScreenSupported()) {
+                continue;
+            }
+            device.setFullScreenWindow(wGreen);
+            sleep();
+            testWindowBounds(device.getDisplayMode(), wGreen);
+            testColor(wGreen, Color.GREEN);
+
+            device.setFullScreenWindow(wRed);
+            sleep();
+            testWindowBounds(device.getDisplayMode(), wRed);
+            testColor(wRed, Color.RED);
+
+            device.setFullScreenWindow(null);
+            sleep();
+            testInsets(wGreen.getInsets(), iGreen);
+            testInsets(wRed.getInsets(), iRed);
+            testSize(wGreen.getSize(), sGreen);
+            testSize(wRed.getSize(), sRed);
+        }
+        wGreen.dispose();
+        wRed.dispose();
+        if (!passed) {
+            throw new RuntimeException("Test failed");
+        }
+    }
+
+    private static void testSize(final Dimension actual, final Dimension exp) {
+        if (!exp.equals(actual)) {
+            System.err.println(" Wrong window size:" +
+                               " Expected: " + exp + " Actual: " + actual);
+            passed = false;
+        }
+    }
+
+    private static void testInsets(final Insets actual, final Insets exp) {
+        if (!actual.equals(exp)) {
+            System.err.println(" Wrong window insets:" +
+                               " Expected: " + exp + " Actual: " + actual);
+            passed = false;
+        }
+    }
+
+    private static void testWindowBounds(final DisplayMode dm, final Window w) {
+        if (w.getWidth() != dm.getWidth() || w.getHeight() != dm.getHeight()) {
+            System.err.println(" Wrong window bounds:" +
+                               " Expected: width = " + dm.getWidth()
+                               + ", height = " + dm.getHeight() + " Actual: "
+                               + w.getSize());
+            passed = false;
+        }
+    }
+
+    private static void testColor(final Window w, final Color color) {
+        final Robot r;
+        try {
+            r = new Robot(w.getGraphicsConfiguration().getDevice());
+        } catch (AWTException e) {
+            e.printStackTrace();
+            passed = false;
+            return;
+        }
+        final BufferedImage bi = r.createScreenCapture(w.getBounds());
+        for (int y = 0; y < bi.getHeight(); y++) {
+            for (int x = 0; x < bi.getWidth(); x++) {
+                if (bi.getRGB(x, y) != color.getRGB()) {
+                    System.err.println(
+                            "Incorrect pixel at " + x + "x" + y + " : " +
+                            Integer.toHexString(bi.getRGB(x, y)) +
+                            " ,expected : " + Integer.toHexString(
+                                    color.getRGB()));
+                    passed = false;
+                    return;
+                }
+            }
+        }
+    }
+
+    private static void sleep() {
+        ((SunToolkit) Toolkit.getDefaultToolkit()).realSync();
+        try {
+            Thread.sleep(2000);
+        } catch (InterruptedException ignored) {
+        }
+    }
+}