8003173: [macosx] Fullscreen on Mac leaves an empty rectangle

Reviewed-by: anthony, alexsch
This commit is contained in:
Sergey Bylokhov 2013-01-24 17:50:03 +04:00
parent d22b9b7149
commit 2c1808e68d
5 changed files with 219 additions and 76 deletions
jdk
src/macosx/classes/sun
test/java/awt/FullScreen/FullScreenInsets

@ -30,6 +30,7 @@ import java.awt.GraphicsDevice;
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 @@ public final class CGraphicsDevice extends GraphicsDevice {
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 @@ public final class CGraphicsDevice extends GraphicsDevice {
}
@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);
}
}
}

@ -53,7 +53,7 @@ public class LWWindowPeer
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 @@ public class LWWindowPeer
/**
* 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 @@ public class LWWindowPeer
}
// Check if anything changed
if (!moved && !resized) {
if (!moved && !resized && !invalid) {
return;
}
// First, update peer's bounds
@ -584,10 +586,10 @@ public class LWWindowPeer
}
// 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 @@ public class LWWindowPeer
}
}
/*
* 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() {

@ -27,7 +27,6 @@ package sun.lwawt.macosx;
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 @@ public class CPlatformView extends CFRetainedResource {
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() {

@ -38,7 +38,6 @@ import sun.awt.*;
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 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor
// 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 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor
@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 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor
@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 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor
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 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor
orderAboveSiblings();
}
private void updateDisplay() {
EventQueue.invokeLater(new Runnable() {
public void run() {
validateSurface();
}
});
}
private void updateWindowContent() {
ComponentEvent resizeEvent = new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED);
SunToolkit.postEvent(SunToolkit.targetToAppContext(target), resizeEvent);
}
private void windowWillEnterFullScreen() {
updateWindowContent();
isFullScreenAnimationOn = true;
}
private void windowDidEnterFullScreen() {
updateDisplay();
isFullScreenAnimationOn = false;
}
private void windowWillExitFullScreen() {
updateWindowContent();
isFullScreenAnimationOn = true;
}
private void windowDidExitFullScreen() {
isFullScreenAnimationOn = false;
}
private void windowDidExitFullScreen() {}
}

@ -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) {
}
}
}