Skip to content
Permalink
Browse files
8267430: GraphicsDevice.setDisplayMode(REFRESH_RATE_UNKNOWN) throws I…
…AE: Unable to set display mode!

Reviewed-by: serb
  • Loading branch information
Phil Race committed Jun 9, 2021
1 parent bf29a01 commit 991ca14279faa7db5d0afe023e666844f5b3b75b
@@ -34,6 +34,7 @@
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 @@

// 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 int getScaleFactor() {
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 boolean isDisplayChangeSupported() {
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 DisplayMode getDisplayMode() {

@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 @@ private void initScaleFactor() {

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);
@@ -261,6 +261,18 @@ static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *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
@@ -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);
}
}
}
}

1 comment on commit 991ca14

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 991ca14 Jun 9, 2021

Please sign in to comment.