8267430: GraphicsDevice.setDisplayMode(REFRESH_RATE_UNKNOWN) throws IAE: Unable to set display mode!

Reviewed-by: serb
This commit is contained in:
Phil Race 2021-06-09 20:52:43 +00:00
parent bf29a0115c
commit 991ca14279
3 changed files with 142 additions and 4 deletions

View File

@ -34,6 +34,7 @@ import java.awt.Rectangle;
import java.awt.Window;
import java.awt.geom.Rectangle2D;
import java.awt.peer.WindowPeer;
import java.util.Arrays;
import java.util.Objects;
import sun.java2d.SunGraphicsEnvironment;
@ -65,9 +66,11 @@ public final class CGraphicsDevice extends GraphicsDevice
// Save/restore DisplayMode for the Full Screen mode
private DisplayMode originalMode;
private DisplayMode initialMode;
public CGraphicsDevice(final int displayID) {
this.displayID = displayID;
this.initialMode = getDisplayMode();
if (MacOSFlags.isMetalEnabled()) {
// Try to create MTLGraphicsConfig, if it fails,
@ -201,6 +204,7 @@ public final class CGraphicsDevice extends GraphicsDevice
public void invalidate(CGraphicsDevice device) {
//TODO do we need to restore the full-screen window/modes on old device?
displayID = device.displayID;
initialMode = device.initialMode;
}
@Override
@ -307,14 +311,47 @@ public final class CGraphicsDevice extends GraphicsDevice
return true;
}
/* If the modes are the same or the only difference is that
* the new mode will match any refresh rate, no need to change.
*/
private boolean isSameMode(final DisplayMode newMode,
final DisplayMode oldMode) {
return (Objects.equals(newMode, oldMode) ||
(newMode.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN &&
newMode.getWidth() == oldMode.getWidth() &&
newMode.getHeight() == oldMode.getHeight() &&
newMode.getBitDepth() == oldMode.getBitDepth()));
}
@Override
public void setDisplayMode(final DisplayMode dm) {
if (dm == null) {
throw new IllegalArgumentException("Invalid display mode");
}
if (!Objects.equals(dm, getDisplayMode())) {
nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(),
dm.getBitDepth(), dm.getRefreshRate());
if (!isSameMode(dm, getDisplayMode())) {
try {
nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(),
dm.getBitDepth(), dm.getRefreshRate());
} catch (Throwable t) {
/* In some cases macOS doesn't report the initial mode
* in the list of supported modes.
* If trying to reset to that mode causes an exception
* try one more time to reset using a different API.
* This does not fix everything, such as it doesn't make
* that mode reported and it restores all devices, but
* this seems a better compromise than failing to restore
*/
if (isSameMode(dm, initialMode)) {
nativeResetDisplayMode();
if (!isSameMode(initialMode, getDisplayMode())) {
throw new IllegalArgumentException(
"Could not reset to initial mode");
}
} else {
throw t;
}
}
}
}
@ -325,7 +362,22 @@ public final class CGraphicsDevice extends GraphicsDevice
@Override
public DisplayMode[] getDisplayModes() {
return nativeGetDisplayModes(displayID);
DisplayMode[] nativeModes = nativeGetDisplayModes(displayID);
boolean match = false;
for (DisplayMode mode : nativeModes) {
if (initialMode.equals(mode)) {
match = true;
break;
}
}
if (match) {
return nativeModes;
} else {
int len = nativeModes.length;
DisplayMode[] modes = Arrays.copyOf(nativeModes, len+1, DisplayMode[].class);
modes[len] = initialMode;
return modes;
}
}
public static boolean usingMetalPipeline() {
@ -345,6 +397,8 @@ public final class CGraphicsDevice extends GraphicsDevice
private static native double nativeGetScaleFactor(int displayID);
private static native void nativeResetDisplayMode();
private static native void nativeSetDisplayMode(int displayID, int w, int h, int bpp, int refrate);
private static native DisplayMode nativeGetDisplayMode(int displayID);

View File

@ -261,6 +261,18 @@ JNI_COCOA_EXIT(env);
return ret;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeResetDisplayMode
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_CGraphicsDevice_nativeResetDisplayMode
(JNIEnv *env, jclass class)
{
CGRestorePermanentDisplayConfiguration();
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeSetDisplayMode

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2021, 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.
*/
/**
* @test
* @bug 8267430
* @key headful
* @summary verify setting a display mode with unknow refresh rate works
*/
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
public class UnknownRefrshRateTest {
public static void main(String[] args) throws Exception {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices = ge.getScreenDevices();
for (GraphicsDevice d : devices) {
if (!d.isDisplayChangeSupported()) {
continue;
}
DisplayMode odm = d.getDisplayMode();
System.out.println("device=" + d + " original mode=" + odm);
DisplayMode[] modes = d.getDisplayModes();
System.out.println("There are " + modes.length + " modes.");
try {
for (int i=0; i<modes.length; i++) {
DisplayMode mode = modes[i];
System.out.println("copying from mode " + i + " : " + mode);
int w = mode.getWidth();
int h = mode.getHeight();
int bpp = mode.getBitDepth();
int refRate = DisplayMode.REFRESH_RATE_UNKNOWN;
DisplayMode newMode = new DisplayMode(w, h, bpp, refRate);
d.setDisplayMode(newMode);
Thread.sleep(2000);
System.out.println("set " + d.getDisplayMode());
}
} finally {
System.out.println("restoring original mode"+odm);
d.setDisplayMode(odm);
Thread.sleep(10000);
}
}
}
}