Path: blob/master/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
66645 views
/*1* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.awt.AWTPermission;28import java.awt.DisplayMode;29import java.awt.GraphicsConfiguration;30import java.awt.GraphicsDevice;31import java.awt.Insets;32import java.awt.Rectangle;33import java.awt.Window;34import java.awt.geom.Rectangle2D;35import java.awt.peer.WindowPeer;36import java.util.Arrays;37import java.util.Objects;3839import sun.java2d.SunGraphicsEnvironment;40import sun.java2d.MacOSFlags;41import sun.java2d.metal.MTLGraphicsConfig;42import sun.java2d.opengl.CGLGraphicsConfig;4344import static java.awt.peer.ComponentPeer.SET_BOUNDS;4546public final class CGraphicsDevice extends GraphicsDevice47implements DisplayChangedListener {4849/**50* CoreGraphics display ID. This identifier can become non-valid at any time51* therefore methods, which is using this id should be ready to it.52*/53private volatile int displayID;54private volatile double xResolution;55private volatile double yResolution;56private volatile Rectangle bounds;57private volatile int scale;5859private GraphicsConfiguration config;60private static boolean metalPipelineEnabled = false;61private static boolean oglPipelineEnabled = false;626364private static AWTPermission fullScreenExclusivePermission;6566// Save/restore DisplayMode for the Full Screen mode67private DisplayMode originalMode;68private DisplayMode initialMode;6970public CGraphicsDevice(final int displayID) {71this.displayID = displayID;72this.initialMode = getDisplayMode();7374if (MacOSFlags.isMetalEnabled()) {75// Try to create MTLGraphicsConfig, if it fails,76// try to create CGLGraphicsConfig as a fallback77this.config = MTLGraphicsConfig.getConfig(this, displayID);7879if (this.config != null) {80metalPipelineEnabled = true;81} else {82// Try falling back to OpenGL pipeline83if (MacOSFlags.isMetalVerbose()) {84System.out.println("Metal rendering pipeline" +85" initialization failed,using OpenGL" +86" rendering pipeline");87}8889this.config = CGLGraphicsConfig.getConfig(this);9091if (this.config != null) {92oglPipelineEnabled = true;93}94}95} else {96// Try to create CGLGraphicsConfig, if it fails,97// try to create MTLGraphicsConfig as a fallback98this.config = CGLGraphicsConfig.getConfig(this);99100if (this.config != null) {101oglPipelineEnabled = true;102} else {103// Try falling back to Metal pipeline104if (MacOSFlags.isOGLVerbose()) {105System.out.println("OpenGL rendering pipeline" +106" initialization failed,using Metal" +107" rendering pipeline");108}109110this.config = MTLGraphicsConfig.getConfig(this, displayID);111112if (this.config != null) {113metalPipelineEnabled = true;114}115}116}117118if (!metalPipelineEnabled && !oglPipelineEnabled) {119// This indicates fallback to other rendering pipeline also failed.120// Should never reach here121throw new InternalError("Error - unable to initialize any" +122" rendering pipeline.");123}124125if (metalPipelineEnabled && MacOSFlags.isMetalVerbose()) {126System.out.println("Metal pipeline enabled on screen " + displayID);127} else if (oglPipelineEnabled && MacOSFlags.isOGLVerbose()) {128System.out.println("OpenGL pipeline enabled on screen " + displayID);129}130131// initializes default device state, might be redundant step since we132// call "displayChanged()" later anyway, but we do not want to leave the133// device in an inconsistent state after construction134displayChanged();135}136137/**138* Return a list of all configurations.139*/140@Override141public GraphicsConfiguration[] getConfigurations() {142return new GraphicsConfiguration[]{config};143}144145/**146* Return the default configuration.147*/148@Override149public GraphicsConfiguration getDefaultConfiguration() {150return config;151}152153/**154* Return a human-readable screen description.155*/156@Override157public String getIDstring() {158return "Display " + displayID;159}160161/**162* Returns the type of the graphics device.163* @see #TYPE_RASTER_SCREEN164* @see #TYPE_PRINTER165* @see #TYPE_IMAGE_BUFFER166*/167@Override168public int getType() {169return TYPE_RASTER_SCREEN;170}171172public double getXResolution() {173return xResolution;174}175176public double getYResolution() {177return yResolution;178}179180Rectangle getBounds() {181return bounds.getBounds();182}183184public Insets getScreenInsets() {185// the insets are queried synchronously and are not cached186// since there are no Quartz or Cocoa means to receive notifications187// on insets changes (e.g. when the Dock is resized):188// the existing CGDisplayReconfigurationCallBack is not notified189// as well as the NSApplicationDidChangeScreenParametersNotification190// is fired on the Dock location changes only191return nativeGetScreenInsets(displayID);192}193194public int getScaleFactor() {195return scale;196}197198/**199* Invalidates this device so it will point to some other "new" device.200*201* @param device the new device, usually the main screen202*/203public void invalidate(CGraphicsDevice device) {204//TODO do we need to restore the full-screen window/modes on old device?205displayID = device.displayID;206initialMode = device.initialMode;207}208209@Override210public void displayChanged() {211xResolution = nativeGetXResolution(displayID);212yResolution = nativeGetYResolution(displayID);213bounds = nativeGetBounds(displayID).getBounds(); //does integer rounding214initScaleFactor();215resizeFSWindow(getFullScreenWindow(), bounds);216//TODO configs?217}218219@Override220public void paletteChanged() {221// devices do not need to react to this event.222}223224/**225* Enters full-screen mode, or returns to windowed mode.226*/227@Override228public synchronized void setFullScreenWindow(Window w) {229Window old = getFullScreenWindow();230if (w == old) {231return;232}233234boolean fsSupported = isFullScreenSupported();235236if (fsSupported && old != null) {237// enter windowed mode and restore original display mode238exitFullScreenExclusive(old);239if (originalMode != null) {240setDisplayMode(originalMode);241originalMode = null;242}243}244245super.setFullScreenWindow(w);246247if (fsSupported && w != null) {248if (isDisplayChangeSupported()) {249originalMode = getDisplayMode();250}251// enter fullscreen mode252enterFullScreenExclusive(w);253}254}255256/**257* Returns true if this GraphicsDevice supports258* full-screen exclusive mode and false otherwise.259*/260@Override261public boolean isFullScreenSupported() {262return isFSExclusiveModeAllowed();263}264265private static boolean isFSExclusiveModeAllowed() {266@SuppressWarnings("removal")267SecurityManager security = System.getSecurityManager();268if (security != null) {269if (fullScreenExclusivePermission == null) {270fullScreenExclusivePermission =271new AWTPermission("fullScreenExclusive");272}273try {274security.checkPermission(fullScreenExclusivePermission);275} catch (SecurityException e) {276return false;277}278}279return true;280}281282private static void enterFullScreenExclusive(Window w) {283FullScreenCapable peer = AWTAccessor.getComponentAccessor().getPeer(w);284if (peer != null) {285peer.enterFullScreenMode();286}287}288289private static void exitFullScreenExclusive(Window w) {290FullScreenCapable peer = AWTAccessor.getComponentAccessor().getPeer(w);291if (peer != null) {292peer.exitFullScreenMode();293}294}295296/**297* Reapplies the size of this device to the full-screen window.298*/299private static void resizeFSWindow(final Window w, final Rectangle b) {300if (w != null) {301WindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);302if (peer != null) {303peer.setBounds(b.x, b.y, b.width, b.height, SET_BOUNDS);304}305}306}307308@Override309public boolean isDisplayChangeSupported() {310return true;311}312313/* If the modes are the same or the only difference is that314* the new mode will match any refresh rate, no need to change.315*/316private boolean isSameMode(final DisplayMode newMode,317final DisplayMode oldMode) {318319return (Objects.equals(newMode, oldMode) ||320(newMode.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN &&321newMode.getWidth() == oldMode.getWidth() &&322newMode.getHeight() == oldMode.getHeight() &&323newMode.getBitDepth() == oldMode.getBitDepth()));324}325326@Override327public void setDisplayMode(final DisplayMode dm) {328if (dm == null) {329throw new IllegalArgumentException("Invalid display mode");330}331if (!isSameMode(dm, getDisplayMode())) {332try {333nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(),334dm.getBitDepth(), dm.getRefreshRate());335} catch (Throwable t) {336/* In some cases macOS doesn't report the initial mode337* in the list of supported modes.338* If trying to reset to that mode causes an exception339* try one more time to reset using a different API.340* This does not fix everything, such as it doesn't make341* that mode reported and it restores all devices, but342* this seems a better compromise than failing to restore343*/344if (isSameMode(dm, initialMode)) {345nativeResetDisplayMode();346if (!isSameMode(initialMode, getDisplayMode())) {347throw new IllegalArgumentException(348"Could not reset to initial mode");349}350} else {351throw t;352}353}354}355}356357@Override358public DisplayMode getDisplayMode() {359return nativeGetDisplayMode(displayID);360}361362@Override363public DisplayMode[] getDisplayModes() {364DisplayMode[] nativeModes = nativeGetDisplayModes(displayID);365boolean match = false;366for (DisplayMode mode : nativeModes) {367if (initialMode.equals(mode)) {368match = true;369break;370}371}372if (match) {373return nativeModes;374} else {375int len = nativeModes.length;376DisplayMode[] modes = Arrays.copyOf(nativeModes, len+1, DisplayMode[].class);377modes[len] = initialMode;378return modes;379}380}381382public static boolean usingMetalPipeline() {383return metalPipelineEnabled;384}385386private void initScaleFactor() {387if (SunGraphicsEnvironment.isUIScaleEnabled()) {388double debugScale = SunGraphicsEnvironment.getDebugScale();389scale = (int) (debugScale >= 1390? Math.round(debugScale)391: nativeGetScaleFactor(displayID));392} else {393scale = 1;394}395}396397private static native double nativeGetScaleFactor(int displayID);398399private static native void nativeResetDisplayMode();400401private static native void nativeSetDisplayMode(int displayID, int w, int h, int bpp, int refrate);402403private static native DisplayMode nativeGetDisplayMode(int displayID);404405private static native DisplayMode[] nativeGetDisplayModes(int displayID);406407private static native double nativeGetXResolution(int displayID);408409private static native double nativeGetYResolution(int displayID);410411private static native Insets nativeGetScreenInsets(int displayID);412413private static native Rectangle2D nativeGetBounds(int displayID);414}415416417