Skip to content
Permalink
Browse files

8176359: Frame#setMaximizedbounds not working properly in multi scree…

…n environments

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

Reviewed-by: aivanov
  • Loading branch information
mrserb committed Jan 26, 2020
1 parent 1af3425 commit bbc3d167059c2679bc01a8dc5a8ca330e6ea4e6e
@@ -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 static Point convertToDeviceSpace(double x, double y) {
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())
);
}
}
@@ -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 @@ public int getExtendedState() {
"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();

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);
}
}
private Rectangle adjustMaximizedBounds(Rectangle bounds) {
// All calculations should be done in the device space
bounds = convertToDeviceSpace(bounds);

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
@@ -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;
@@ -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 @@ class AwtFrame : public AwtWindow {
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);

@@ -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
@@ -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();
}
}
}
}
}

0 comments on commit bbc3d16

Please sign in to comment.