8176359: Frame#setMaximizedbounds not working properly in multi screen environments

8231564: setMaximizedBounds is broken with large display scale and multiple monitors

Reviewed-by: aivanov
This commit is contained in:
Sergey Bylokhov 2020-01-25 22:46:20 -08:00
parent 1af34250aa
commit bbc3d16705
8 changed files with 305 additions and 60 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2020, 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
@ -364,7 +364,38 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment
AffineTransform tx = gc.getDefaultTransform();
x = Region.clipRound(x * tx.getScaleX());
y = Region.clipRound(y * tx.getScaleY());
return new Point((int) x, (int) y);
}
/**
* Converts bounds from the user's space to the device space using
* appropriate device transformation.
*
* @param bounds the rectangle in the user space
* @return the rectangle which uses device space(pixels)
*/
public static Rectangle convertToDeviceSpace(Rectangle bounds) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, bounds.x, bounds.y);
return convertToDeviceSpace(gc, bounds);
}
/**
* Converts bounds from the user's space to the device space using
* appropriate device transformation of the passed graphics configuration.
*
* @param bounds the rectangle in the user space
* @return the rectangle which uses device space(pixels)
*/
public static Rectangle convertToDeviceSpace(GraphicsConfiguration gc,
Rectangle bounds) {
AffineTransform tx = gc.getDefaultTransform();
return new Rectangle(
Region.clipRound(bounds.x * tx.getScaleX()),
Region.clipRound(bounds.y * tx.getScaleY()),
Region.clipRound(bounds.width * tx.getScaleX()),
Region.clipRound(bounds.height * tx.getScaleY())
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2020, 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
@ -22,15 +22,24 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.awt.windows;
import java.awt.*;
import java.awt.peer.*;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.MenuBar;
import java.awt.Rectangle;
import java.awt.peer.FramePeer;
import java.security.AccessController;
import sun.awt.AWTAccessor;
import sun.awt.im.InputMethodManager;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;
import static sun.java2d.SunGraphicsEnvironment.convertToDeviceSpace;
class WFramePeer extends WWindowPeer implements FramePeer {
static {
@ -65,49 +74,51 @@ class WFramePeer extends WWindowPeer implements FramePeer {
"sun.awt.keepWorkingSetOnMinimize")));
@Override
public void setMaximizedBounds(Rectangle b) {
public final void setMaximizedBounds(Rectangle b) {
if (b == null) {
clearMaximizedBounds();
} else {
Rectangle adjBounds = (Rectangle)b.clone();
adjustMaximizedBounds(adjBounds);
setMaximizedBounds(adjBounds.x, adjBounds.y, adjBounds.width, adjBounds.height);
b = adjustMaximizedBounds(b);
setMaximizedBounds(b.x, b.y, b.width, b.height);
}
}
/**
* The incoming bounds describe the maximized size and position of the
* window on the monitor that displays the window. But the window manager
* expects that the bounds are based on the size and position of the
* primary monitor, even if the window ultimately maximizes onto a
* secondary monitor. And the window manager adjusts these values to
* compensate for differences between the primary monitor and the monitor
* that displays the window.
* window in the virtual coordinate system. But the window manager expects
* that the bounds are based on the size of the primary monitor and
* position is based on the actual window monitor, even if the window
* ultimately maximizes onto a secondary monitor. And the window manager
* adjusts these values to compensate for differences between the primary
* monitor and the monitor that displays the window.
* <p>
* The method translates the incoming bounds to the values acceptable
* by the window manager. For more details, please refer to 6699851.
*/
private void adjustMaximizedBounds(Rectangle b) {
GraphicsConfiguration currentDevGC = getGraphicsConfiguration();
private Rectangle adjustMaximizedBounds(Rectangle bounds) {
// All calculations should be done in the device space
bounds = convertToDeviceSpace(bounds);
GraphicsDevice primaryDev = GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice();
GraphicsConfiguration primaryDevGC = primaryDev.getDefaultConfiguration();
if (currentDevGC != null && currentDevGC != primaryDevGC) {
Rectangle currentDevBounds = currentDevGC.getBounds();
Rectangle primaryDevBounds = primaryDevGC.getBounds();
boolean isCurrentDevLarger =
((currentDevBounds.width - primaryDevBounds.width > 0) ||
(currentDevBounds.height - primaryDevBounds.height > 0));
// the window manager doesn't seem to compensate for differences when
// the primary monitor is larger than the monitor that display the window
if (isCurrentDevLarger) {
b.width -= (currentDevBounds.width - primaryDevBounds.width);
b.height -= (currentDevBounds.height - primaryDevBounds.height);
}
}
GraphicsConfiguration gc = getGraphicsConfiguration();
Rectangle currentDevBounds = convertToDeviceSpace(gc, gc.getBounds());
// Prepare data for WM_GETMINMAXINFO message.
// ptMaxPosition should be in coordinate system of the current monitor,
// not the main monitor, or monitor on which we maximize the window.
bounds.x -= currentDevBounds.x;
bounds.y -= currentDevBounds.y;
// ptMaxSize will be used as-is if the size is smaller than the main
// monitor. If the size is larger than the main monitor then the
// window manager adjusts the size, like this:
// result = bounds.w + (current.w - main.w); =>> wrong size
// We can try to compensate for this adjustment like this:
// result = bounds.w - (current.w - main.w);
// but this can result to the size smaller than the main screen, so no
// adjustment will be done by the window manager =>> wrong size.
// So we skip compensation here and cut the adjustment on
// WM_WINDOWPOSCHANGING event.
// Note that the result does not depend on the monitor on which we
// maximize the window.
return bounds;
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2020, 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
@ -897,6 +897,21 @@ MsgRouting AwtFrame::WmGetMinMaxInfo(LPMINMAXINFO lpmmi)
return mrConsume;
}
MsgRouting AwtFrame::WmWindowPosChanging(LPARAM windowPos) {
if (::IsZoomed(GetHWnd()) && m_maxBoundsSet) {
// Limits the size of the maximized window, effectively cuts the
// adjustments added by the window manager
WINDOWPOS *wp = (WINDOWPOS *) windowPos;
if (m_maxSize.x < java_lang_Integer_MAX_VALUE && wp->cx > m_maxSize.x) {
wp->cx = m_maxSize.x;
}
if (m_maxSize.y < java_lang_Integer_MAX_VALUE && wp->cy > m_maxSize.y) {
wp->cy = m_maxSize.y;
}
}
return AwtWindow::WmWindowPosChanging(windowPos);
}
MsgRouting AwtFrame::WmSize(UINT type, int w, int h)
{
currentWmSizeState = type;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2020, 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
@ -116,6 +116,7 @@ public:
MsgRouting WmNcMouseUp(WPARAM hitTest, int x, int y, int button);
MsgRouting WmGetIcon(WPARAM iconType, LRESULT& retVal);
MsgRouting WmShowWindow(BOOL show, UINT status);
MsgRouting WmWindowPosChanging(LPARAM windowPos);
virtual MsgRouting WmSysCommand(UINT uCmdType, int xPos, int yPos);

View File

@ -1,6 +1,6 @@
###########################################################################
#
# Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2009, 2020, 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
@ -211,7 +211,6 @@ java/awt/event/KeyEvent/KeyMaskTest/KeyMaskTest.java 8129778 generic-all
java/awt/event/MouseEvent/MouseButtonsAndKeyMasksTest/MouseButtonsAndKeyMasksTest.java 8129778 generic-all
java/awt/dnd/URIListToFileListBetweenJVMsTest/URIListToFileListBetweenJVMsTest.java 8194947 generic-all
java/awt/Frame/SetMaximizedBounds/SetMaximizedBounds.java 8196006 windows-all
java/awt/Frame/FramesGC/FramesGC.java 8079069 macosx-all
java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java 8047218 generic-all
java/awt/GridLayout/LayoutExtraGaps/LayoutExtraGaps.java 8000171 windows-all

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020, 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.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
/**
* @test
* @bug 8176359 8231564
* @key headful
* @requires (os.family == "windows" | os.family == "mac")
* @summary setMaximizedBounds() should work if set to the screen other than
* current screen of the Frame, the size of the frame is intentionally
* big
* @run main/othervm MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=1 MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=1.2 MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=1.25 MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=1.5 MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=1.75 MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=2 MaximizedToOppositeScreenBig
* @run main/othervm -Dsun.java2d.uiScale=2.25 MaximizedToOppositeScreenBig
*/
public final class MaximizedToOppositeScreenBig {
public static void main(String[] args) throws Exception {
//Supported platforms are Windows and OS X.
String os = System.getProperty("os.name").toLowerCase();
if (!os.contains("windows") && !os.contains("os x")) {
return;
}
if (!Toolkit.getDefaultToolkit().
isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
return;
}
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gds = ge.getScreenDevices();
Robot robot = new Robot();
for (GraphicsDevice gd1 : gds) {
Rectangle framAt = gd1.getDefaultConfiguration().getBounds();
framAt.grow(-200, -200);
for (GraphicsDevice gd2 : gds) {
Rectangle maxTo = gd2.getDefaultConfiguration().getBounds();
maxTo.grow(-150, -150);
Frame frame = new Frame(gd1.getDefaultConfiguration());
try {
frame.setBounds(framAt);
frame.setVisible(true);
robot.waitForIdle();
robot.delay(1000);
frame.setMaximizedBounds(maxTo);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
robot.waitForIdle();
robot.delay(1000);
Rectangle actual = frame.getBounds();
if (!actual.equals(maxTo)) {
System.err.println("Actual: " + actual);
System.err.println("Expected: " + maxTo);
throw new RuntimeException("Wrong bounds");
}
} finally {
frame.dispose();
}
}
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2020, 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.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
/**
* @test
* @bug 8176359 8231564
* @key headful
* @requires (os.family == "windows" | os.family == "mac")
* @summary setMaximizedBounds() should work if set to the screen other than
* current screen of the Frame, the size of the frame is intentionally
* small
*/
public final class MaximizedToOppositeScreenSmall {
public static void main(String[] args) throws Exception {
//Supported platforms are Windows and OS X.
String os = System.getProperty("os.name").toLowerCase();
if (!os.contains("windows") && !os.contains("os x")) {
return;
}
if (!Toolkit.getDefaultToolkit().
isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
return;
}
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gds = ge.getScreenDevices();
Robot robot = new Robot();
for (GraphicsDevice gd1 : gds) {
Rectangle framAt = gd1.getDefaultConfiguration().getBounds();
framAt.grow(-framAt.width / 2 + 100, -framAt.height / 2 + 100);
for (GraphicsDevice gd2 : gds) {
Rectangle maxTo = gd2.getDefaultConfiguration().getBounds();
maxTo.grow(-maxTo.width / 2 + 120, -maxTo.height / 2 + 120);
Frame frame = new Frame(gd1.getDefaultConfiguration());
try {
frame.setBounds(framAt);
frame.setVisible(true);
robot.waitForIdle();
robot.delay(1000);
frame.setMaximizedBounds(maxTo);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
robot.waitForIdle();
robot.delay(1000);
Rectangle actual = frame.getBounds();
if (!actual.equals(maxTo)) {
System.err.println("Actual: " + actual);
System.err.println("Expected: " + maxTo);
throw new RuntimeException("Wrong bounds");
}
} finally {
frame.dispose();
}
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2020, 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
@ -21,23 +21,27 @@
* questions.
*/
import java.awt.*;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
/**
* @test
* @key headful
* @bug 8065739 8131339
* @bug 8065739 8131339 8231564
* @requires (os.family == "windows" | os.family == "mac")
* @summary When Frame.setExtendedState(Frame.MAXIMIZED_BOTH)
* is called for a Frame after been called setMaximizedBounds() with
* certain value, Frame bounds must equal to this value.
*
* @run main SetMaximizedBounds
*/
public class SetMaximizedBounds {
public static void main(String[] args) throws Exception {
//Supported platforms are Windows and OS X.
String os = System.getProperty("os.name").toLowerCase();
if (!os.contains("windows") && !os.contains("os x")) {
@ -52,10 +56,6 @@ public class SetMaximizedBounds {
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
if (ge.isHeadlessInstance()) {
return;
}
for (GraphicsDevice gd : ge.getScreenDevices()) {
for (GraphicsConfiguration gc : gd.getConfigurations()) {
testMaximizedBounds(gc, false);
@ -78,10 +78,10 @@ public class SetMaximizedBounds {
frame = new Frame();
frame.setUndecorated(undecorated);
Rectangle maximizedBounds = new Rectangle(
maxArea.x + maxArea.width / 6,
maxArea.y + maxArea.height / 6,
maxArea.width / 3,
maxArea.height / 3);
maxArea.x + maxArea.width / 5,
maxArea.y + maxArea.height / 5,
maxArea.width / 2,
maxArea.height / 2);
frame.setMaximizedBounds(maximizedBounds);
frame.setSize(maxArea.width / 8, maxArea.height / 8);
frame.setVisible(true);
@ -93,6 +93,8 @@ public class SetMaximizedBounds {
Rectangle bounds = frame.getBounds();
if (!bounds.equals(maximizedBounds)) {
System.err.println("Expected: " + maximizedBounds);
System.err.println("Actual: " + bounds);
throw new RuntimeException("The bounds of the Frame do not equal to what"
+ " is specified when the frame is in Frame.MAXIMIZED_BOTH state");
}
@ -102,10 +104,10 @@ public class SetMaximizedBounds {
robot.delay(1000);
maximizedBounds = new Rectangle(
maxArea.x + maxArea.width / 10,
maxArea.y + maxArea.height / 10,
maxArea.width / 5,
maxArea.height / 5);
maxArea.x + maxArea.width / 6,
maxArea.y + maxArea.height / 6,
maxArea.width / 3,
maxArea.height / 3);
frame.setMaximizedBounds(maximizedBounds);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
robot.waitForIdle();
@ -113,6 +115,8 @@ public class SetMaximizedBounds {
bounds = frame.getBounds();
if (!bounds.equals(maximizedBounds)) {
System.err.println("Expected: " + maximizedBounds);
System.err.println("Actual: " + bounds);
throw new RuntimeException("The bounds of the Frame do not equal to what"
+ " is specified when the frame is in Frame.MAXIMIZED_BOTH state");
}