| /* | |
| * Copyright (c) 1997, 2024, 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. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * 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. | |
| */ | |
| package sun.awt; | |
| import java.awt.DisplayMode; | |
| import java.awt.EventQueue; | |
| import java.awt.Frame; | |
| import java.awt.GraphicsConfiguration; | |
| import java.awt.GraphicsDevice; | |
| import java.awt.GraphicsEnvironment; | |
| import java.awt.Rectangle; | |
| import java.awt.Window; | |
| import java.awt.event.WindowAdapter; | |
| import java.awt.event.WindowEvent; | |
| import java.awt.event.WindowListener; | |
| import java.awt.image.ColorModel; | |
| import java.awt.peer.WindowPeer; | |
| import java.util.ArrayList; | |
| import sun.awt.image.SurfaceManager; | |
| import sun.awt.windows.WWindowPeer; | |
| import sun.java2d.SunGraphicsEnvironment; | |
| import sun.java2d.opengl.WGLGraphicsConfig; | |
| import sun.java2d.windows.WindowsFlags; | |
| import static java.awt.peer.ComponentPeer.SET_BOUNDS; | |
| import static sun.awt.Win32GraphicsEnvironment.debugScaleX; | |
| import static sun.awt.Win32GraphicsEnvironment.debugScaleY; | |
| /** | |
| * This is an implementation of a GraphicsDevice object for a single | |
| * Win32 screen. | |
| * | |
| * @see GraphicsEnvironment | |
| * @see GraphicsConfiguration | |
| */ | |
| public class Win32GraphicsDevice extends GraphicsDevice implements | |
| DisplayChangedListener { | |
| int screen; | |
| ColorModel dynamicColorModel; // updated with dev changes | |
| ColorModel colorModel; // static for device | |
| protected GraphicsConfiguration[] configs; | |
| protected GraphicsConfiguration defaultConfig; | |
| private final String idString; | |
| protected String descString; | |
| // Note that we do not synchronize access to this variable - it doesn't | |
| // really matter if a thread does an operation on graphics device which is | |
| // about to become invalid (or already become) - we are prepared to deal | |
| // with this on the native level. | |
| private boolean valid; | |
| // keep track of top-level windows on this display | |
| private SunDisplayChanger topLevels = new SunDisplayChanger(); | |
| // REMIND: we may disable the use of pixel formats for some accelerated | |
| // pipelines which are mutually exclusive with opengl, for which | |
| // pixel formats were added in the first place | |
| protected static boolean pfDisabled; | |
| // the original display mode we had before entering the fullscreen | |
| // mode | |
| private DisplayMode defaultDisplayMode; | |
| // activation/deactivation listener for the full-screen window | |
| private WindowListener fsWindowListener; | |
| private float scaleX; | |
| private float scaleY; | |
| final SurfaceManager.ProxyCache surfaceDataProxyCache = | |
| new SurfaceManager.ProxyCache(); | |
| static { | |
| // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when | |
| // pixel format calls are made. This causes problems when a Java app | |
| // is run as an NT service. To prevent the loading of ddraw.dll | |
| // completely, sun.awt.nopixfmt should be set as well. Apps which use | |
| // OpenGL w/ Java probably don't want to set this. | |
| String nopixfmt = System.getProperty("sun.awt.nopixfmt"); | |
| pfDisabled = (nopixfmt != null); | |
| initIDs(); | |
| } | |
| private static native void initIDs(); | |
| native void initDevice(int screen); | |
| native void initNativeScale(int screen); | |
| native void setNativeScale(int screen, float scaleX, float scaleY); | |
| native float getNativeScaleX(int screen); | |
| native float getNativeScaleY(int screen); | |
| public Win32GraphicsDevice(int screennum) { | |
| this.screen = screennum; | |
| // we cache the strings because we want toString() and getIDstring | |
| // to reflect the original screen number (which may change if the | |
| // device is removed) | |
| idString = "\\Display"+screen; | |
| // REMIND: may be should use class name? | |
| descString = "Win32GraphicsDevice[screen=" + screen; | |
| valid = true; | |
| initDevice(screennum); | |
| initScaleFactors(); | |
| } | |
| /** | |
| * Returns the type of the graphics device. | |
| * @see #TYPE_RASTER_SCREEN | |
| * @see #TYPE_PRINTER | |
| * @see #TYPE_IMAGE_BUFFER | |
| */ | |
| @Override | |
| public int getType() { | |
| return TYPE_RASTER_SCREEN; | |
| } | |
| /** | |
| * Returns the Win32 screen of the device. | |
| */ | |
| public int getScreen() { | |
| return screen; | |
| } | |
| public float getDefaultScaleX() { | |
| return scaleX; | |
| } | |
| public float getDefaultScaleY() { | |
| return scaleY; | |
| } | |
| private void initScaleFactors() { | |
| if (SunGraphicsEnvironment.isUIScaleEnabled()) { | |
| if (debugScaleX > 0 && debugScaleY > 0) { | |
| scaleX = debugScaleX; | |
| scaleY = debugScaleY; | |
| setNativeScale(screen, scaleX, scaleY); | |
| } else { | |
| initNativeScale(screen); | |
| scaleX = getNativeScaleX(screen); | |
| scaleY = getNativeScaleY(screen); | |
| } | |
| } else { | |
| scaleX = 1; | |
| scaleY = 1; | |
| } | |
| } | |
| /** | |
| * Returns whether this is a valid device. Device can become | |
| * invalid as a result of device removal event. | |
| */ | |
| public boolean isValid() { | |
| return valid; | |
| } | |
| /** | |
| * Called from native code when the device was removed. | |
| * | |
| * @param defaultScreen the current default screen | |
| */ | |
| protected void invalidate(int defaultScreen) { | |
| valid = false; | |
| screen = defaultScreen; | |
| } | |
| /** | |
| * Returns the identification string associated with this graphics | |
| * device. | |
| */ | |
| @Override | |
| public String getIDstring() { | |
| return idString; | |
| } | |
| /** | |
| * Returns all of the graphics | |
| * configurations associated with this graphics device. | |
| */ | |
| @Override | |
| public GraphicsConfiguration[] getConfigurations() { | |
| if (configs==null) { | |
| if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) { | |
| defaultConfig = getDefaultConfiguration(); | |
| if (defaultConfig != null) { | |
| configs = new GraphicsConfiguration[1]; | |
| configs[0] = defaultConfig; | |
| return configs.clone(); | |
| } | |
| } | |
| int max = getMaxConfigs(screen); | |
| int defaultPixID = getDefaultPixID(screen); | |
| ArrayList<GraphicsConfiguration> v = new ArrayList<>( max ); | |
| if (defaultPixID == 0) { | |
| // Workaround for failing GDI calls | |
| defaultConfig = Win32GraphicsConfig.getConfig(this, | |
| defaultPixID); | |
| v.add(defaultConfig); | |
| } | |
| else { | |
| for (int i = 1; i <= max; i++) { | |
| if (isPixFmtSupported(i, screen)) { | |
| if (i == defaultPixID) { | |
| defaultConfig = Win32GraphicsConfig.getConfig( | |
| this, i); | |
| v.add(defaultConfig); | |
| } | |
| else { | |
| v.add(Win32GraphicsConfig.getConfig( | |
| this, i)); | |
| } | |
| } | |
| } | |
| } | |
| configs = v.toArray(new GraphicsConfiguration[0]); | |
| } | |
| return configs.clone(); | |
| } | |
| /** | |
| * Returns the maximum number of graphics configurations available, or 1 | |
| * if PixelFormat calls fail or are disabled. | |
| * This number is less than or equal to the number of graphics | |
| * configurations supported. | |
| */ | |
| protected int getMaxConfigs(int screen) { | |
| if (pfDisabled) { | |
| return 1; | |
| } else { | |
| return getMaxConfigsImpl(screen); | |
| } | |
| } | |
| private native int getMaxConfigsImpl(int screen); | |
| /** | |
| * Returns whether or not the PixelFormat indicated by index is | |
| * supported. Supported PixelFormats support drawing to a Window | |
| * (PFD_DRAW_TO_WINDOW), support GDI (PFD_SUPPORT_GDI), and in the | |
| * case of an 8-bit format (cColorBits <= 8) uses indexed colors | |
| * (iPixelType == PFD_TYPE_COLORINDEX). | |
| * We use the index 0 to indicate that PixelFormat calls don't work, or | |
| * are disabled. Do not call this function with an index of 0. | |
| * @param index a PixelFormat index | |
| */ | |
| private native boolean isPixFmtSupported(int index, int screen); | |
| /** | |
| * Returns the PixelFormatID of the default graphics configuration | |
| * associated with this graphics device, or 0 if PixelFormats calls fail or | |
| * are disabled. | |
| */ | |
| protected int getDefaultPixID(int screen) { | |
| if (pfDisabled) { | |
| return 0; | |
| } else { | |
| return getDefaultPixIDImpl(screen); | |
| } | |
| } | |
| /** | |
| * Returns the default PixelFormat ID from GDI. Do not call if PixelFormats | |
| * are disabled. | |
| */ | |
| private native int getDefaultPixIDImpl(int screen); | |
| /** | |
| * Returns the default graphics configuration | |
| * associated with this graphics device. | |
| */ | |
| @Override | |
| public GraphicsConfiguration getDefaultConfiguration() { | |
| if (defaultConfig == null) { | |
| // first try to create a WGLGraphicsConfig if OGL is enabled | |
| // REMIND: the WGL code does not yet work properly in multimon | |
| // situations, so we will fallback on GDI if we are not on the | |
| // default device... | |
| if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) { | |
| int defPixID = WGLGraphicsConfig.getDefaultPixFmt(screen); | |
| defaultConfig = WGLGraphicsConfig.getConfig(this, defPixID); | |
| if (WindowsFlags.isOGLVerbose()) { | |
| if (defaultConfig != null) { | |
| System.out.print("OpenGL pipeline enabled"); | |
| } else { | |
| System.out.print("Could not enable OpenGL pipeline"); | |
| } | |
| System.out.println(" for default config on screen " + | |
| screen); | |
| } | |
| } | |
| // Fix for 4669614. Most apps are not concerned with PixelFormats, | |
| // yet we ALWAYS used them for determining ColorModels and such. | |
| // By passing in 0 as the PixelFormatID here, we signal that | |
| // PixelFormats should not be used, thus avoid loading the opengl | |
| // library. Apps concerned with PixelFormats can still use | |
| // GraphicsConfiguration.getConfigurations(). | |
| // Note that calling native pixel format functions tends to cause | |
| // problems between those functions (which are OpenGL-related) | |
| // and our use of DirectX. For example, some Matrox boards will | |
| // crash or hang calling these functions when any app is running | |
| // in DirectX fullscreen mode. So avoiding these calls unless | |
| // absolutely necessary is preferable. | |
| if (defaultConfig == null) { | |
| defaultConfig = Win32GraphicsConfig.getConfig(this, 0); | |
| } | |
| } | |
| return defaultConfig; | |
| } | |
| @Override | |
| public String toString() { | |
| return valid ? descString + "]" : descString + ", removed]"; | |
| } | |
| /** | |
| * Returns true if this is the default GraphicsDevice for the | |
| * GraphicsEnvironment. | |
| */ | |
| private boolean isDefaultDevice() { | |
| return (this == | |
| GraphicsEnvironment. | |
| getLocalGraphicsEnvironment().getDefaultScreenDevice()); | |
| } | |
| /** | |
| * returns true unless we're not allowed to use fullscreen mode. | |
| */ | |
| @Override | |
| public boolean isFullScreenSupported() { | |
| return true; | |
| } | |
| @Override | |
| public synchronized void setFullScreenWindow(Window w) { | |
| Window old = getFullScreenWindow(); | |
| if (w == old) { | |
| return; | |
| } | |
| if (!isFullScreenSupported()) { | |
| super.setFullScreenWindow(w); | |
| return; | |
| } | |
| // Enter windowed mode. | |
| if (old != null) { | |
| // restore the original display mode | |
| if (defaultDisplayMode != null) { | |
| setDisplayMode(defaultDisplayMode); | |
| // we set the default display mode to null here | |
| // because the default mode could change during | |
| // the life of the application (user can change it through | |
| // the desktop properties dialog, for example), so | |
| // we need to record it every time prior to | |
| // entering the fullscreen mode. | |
| defaultDisplayMode = null; | |
| } | |
| WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(old); | |
| if (peer != null) { | |
| peer.setFullScreenExclusiveModeState(false); | |
| // we used to destroy the buffers on exiting fs mode, this | |
| // is no longer needed since fs change will cause a surface | |
| // data replacement | |
| synchronized(peer) { | |
| exitFullScreenExclusive(screen, peer); | |
| } | |
| } | |
| removeFSWindowListener(old); | |
| } | |
| super.setFullScreenWindow(w); | |
| if (w != null) { | |
| // always record the default display mode prior to going | |
| // fullscreen | |
| defaultDisplayMode = getDisplayMode(); | |
| addFSWindowListener(w); | |
| // Enter full screen exclusive mode. | |
| WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); | |
| if (peer != null) { | |
| synchronized(peer) { | |
| enterFullScreenExclusive(screen, peer); | |
| // Note: removed replaceSurfaceData() call because | |
| // changing the window size or making it visible | |
| // will cause this anyway, and both of these events happen | |
| // as part of switching into fullscreen mode. | |
| } | |
| peer.setFullScreenExclusiveModeState(true); | |
| } | |
| // fix for 4868278 | |
| peer.updateGC(); | |
| } | |
| } | |
| // Entering and exiting full-screen mode are done within a | |
| // tree-lock and should never lock on any resources which are | |
| // required by other threads which may have them and may require | |
| // the tree-lock. | |
| // REMIND: in the future these methods may need to become protected so that | |
| // subclasses could override them and use appropriate api other than GDI | |
| // for implementing these functions. | |
| protected native void enterFullScreenExclusive(int screen, WindowPeer w); | |
| protected native void exitFullScreenExclusive(int screen, WindowPeer w); | |
| /** | |
| * Reapplies the size of this graphics device to | |
| * the given full-screen window. | |
| * @param w a Window that needs resizing | |
| * @param b new full-screen window bounds | |
| */ | |
| private static void resizeFSWindow(final Window w, final Rectangle b) { | |
| if (w != null) { | |
| WindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); | |
| if (peer != null) { | |
| peer.setBounds(b.x, b.y, b.width, b.height, SET_BOUNDS); | |
| } | |
| } | |
| } | |
| @Override | |
| public boolean isDisplayChangeSupported() { | |
| return (isFullScreenSupported() && getFullScreenWindow() != null); | |
| } | |
| @Override | |
| public synchronized void setDisplayMode(DisplayMode dm) { | |
| if (!isDisplayChangeSupported()) { | |
| super.setDisplayMode(dm); | |
| return; | |
| } | |
| if (dm == null || (dm = getMatchingDisplayMode(dm)) == null) { | |
| throw new IllegalArgumentException("Invalid display mode"); | |
| } | |
| if (getDisplayMode().equals(dm)) { | |
| return; | |
| } | |
| Window w = getFullScreenWindow(); | |
| if (w != null) { | |
| WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); | |
| configDisplayMode(screen, peer, dm.getWidth(), dm.getHeight(), | |
| dm.getBitDepth(), dm.getRefreshRate()); | |
| // Note: the full-screen window will get resized to the dimensions of the new | |
| // display mode in the upcoming display change event, when the DPI scales | |
| // would already be correctly set etc. | |
| } else { | |
| throw new IllegalStateException("Must be in fullscreen mode " + | |
| "in order to set display mode"); | |
| } | |
| } | |
| protected native DisplayMode getCurrentDisplayMode(int screen); | |
| protected native void configDisplayMode(int screen, WindowPeer w, int width, | |
| int height, int bitDepth, | |
| int refreshRate); | |
| protected native void enumDisplayModes(int screen, ArrayList<DisplayMode> modes); | |
| @Override | |
| public synchronized DisplayMode getDisplayMode() { | |
| DisplayMode res = getCurrentDisplayMode(screen); | |
| return res; | |
| } | |
| @Override | |
| public synchronized DisplayMode[] getDisplayModes() { | |
| ArrayList<DisplayMode> modes = new ArrayList<>(); | |
| enumDisplayModes(screen, modes); | |
| int listSize = modes.size(); | |
| DisplayMode[] retArray = new DisplayMode[listSize]; | |
| for (int i = 0; i < listSize; i++) { | |
| retArray[i] = modes.get(i); | |
| } | |
| return retArray; | |
| } | |
| protected synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { | |
| if (!isDisplayChangeSupported()) { | |
| return null; | |
| } | |
| DisplayMode[] modes = getDisplayModes(); | |
| for (DisplayMode mode : modes) { | |
| if (dm.equals(mode) || | |
| (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN && | |
| dm.getWidth() == mode.getWidth() && | |
| dm.getHeight() == mode.getHeight() && | |
| dm.getBitDepth() == mode.getBitDepth())) | |
| { | |
| return mode; | |
| } | |
| } | |
| return null; | |
| } | |
| /* | |
| * From the DisplayChangeListener interface. | |
| * Called from Win32GraphicsEnvironment when the display settings have | |
| * changed. | |
| */ | |
| @Override | |
| public void displayChanged() { | |
| dynamicColorModel = null; | |
| defaultConfig = null; | |
| configs = null; | |
| initScaleFactors(); | |
| Rectangle screenBounds = getDefaultConfiguration().getBounds(); | |
| resizeFSWindow(getFullScreenWindow(), screenBounds); | |
| // pass on to all top-level windows on this display | |
| topLevels.notifyListeners(); | |
| } | |
| /** | |
| * Part of the DisplayChangedListener interface: devices | |
| * do not need to react to this event | |
| */ | |
| @Override | |
| public void paletteChanged() { | |
| } | |
| /* | |
| * Add a DisplayChangeListener to be notified when the display settings | |
| * are changed. Typically, only top-level containers need to be added | |
| * to Win32GraphicsDevice. | |
| */ | |
| public void addDisplayChangedListener(DisplayChangedListener client) { | |
| topLevels.add(client); | |
| } | |
| /* | |
| * Remove a DisplayChangeListener from this Win32GraphicsDevice | |
| */ | |
| public void removeDisplayChangedListener(DisplayChangedListener client) { | |
| topLevels.remove(client); | |
| } | |
| /** | |
| * Creates and returns the color model associated with this device | |
| */ | |
| private native ColorModel makeColorModel (int screen, | |
| boolean dynamic); | |
| /** | |
| * Returns a dynamic ColorModel which is updated when there | |
| * are any changes (e.g., palette changes) in the device | |
| */ | |
| public ColorModel getDynamicColorModel() { | |
| if (dynamicColorModel == null) { | |
| dynamicColorModel = makeColorModel(screen, true); | |
| } | |
| return dynamicColorModel; | |
| } | |
| /** | |
| * Returns the non-dynamic ColorModel associated with this device | |
| */ | |
| public ColorModel getColorModel() { | |
| if (colorModel == null) { | |
| colorModel = makeColorModel(screen, false); | |
| } | |
| return colorModel; | |
| } | |
| /** | |
| * WindowAdapter class responsible for de/iconifying full-screen window | |
| * of this device. | |
| * | |
| * The listener restores the default display mode when window is iconified | |
| * and sets it back to the one set by the user on de-iconification. | |
| */ | |
| private static final class Win32FSWindowAdapter extends WindowAdapter { | |
| private Win32GraphicsDevice device; | |
| private DisplayMode dm; | |
| Win32FSWindowAdapter(Win32GraphicsDevice device) { | |
| this.device = device; | |
| } | |
| private void setFSWindowsState(Window other, int state) { | |
| GraphicsDevice[] gds = | |
| GraphicsEnvironment.getLocalGraphicsEnvironment(). | |
| getScreenDevices(); | |
| // check if the de/activation was caused by other | |
| // fs window and ignore the event if that's the case | |
| if (other != null) { | |
| for (GraphicsDevice gd : gds) { | |
| if (other == gd.getFullScreenWindow()) { | |
| return; | |
| } | |
| } | |
| } | |
| // otherwise apply state to all fullscreen windows | |
| for (GraphicsDevice gd : gds) { | |
| Window fsw = gd.getFullScreenWindow(); | |
| if (fsw instanceof Frame) { | |
| ((Frame)fsw).setExtendedState(state); | |
| } | |
| } | |
| } | |
| @Override | |
| public void windowDeactivated(WindowEvent e) { | |
| setFSWindowsState(e.getOppositeWindow(), Frame.ICONIFIED); | |
| } | |
| @Override | |
| public void windowActivated(WindowEvent e) { | |
| setFSWindowsState(e.getOppositeWindow(), Frame.NORMAL); | |
| } | |
| @Override | |
| public void windowIconified(WindowEvent e) { | |
| // restore the default display mode for this device | |
| DisplayMode ddm = device.defaultDisplayMode; | |
| if (ddm != null) { | |
| dm = device.getDisplayMode(); | |
| device.setDisplayMode(ddm); | |
| } | |
| } | |
| @Override | |
| public void windowDeiconified(WindowEvent e) { | |
| // restore the user-set display mode for this device | |
| if (dm != null) { | |
| device.setDisplayMode(dm); | |
| dm = null; | |
| } | |
| } | |
| } | |
| /** | |
| * Adds a WindowListener to be used as | |
| * activation/deactivation listener for the current full-screen window. | |
| * | |
| * @param w full-screen window | |
| */ | |
| protected void addFSWindowListener(final Window w) { | |
| // Note: even though we create a listener for Window instances of | |
| // fs windows they will not receive window events. | |
| fsWindowListener = new Win32FSWindowAdapter(this); | |
| // Fix for 6709453. Using invokeLater to avoid listening | |
| // for the events already posted to the queue. | |
| EventQueue.invokeLater(new Runnable() { | |
| @Override | |
| public void run() { | |
| w.addWindowListener(fsWindowListener); | |
| } | |
| }); | |
| } | |
| /** | |
| * Removes the fs window listener. | |
| * | |
| * @param w full-screen window | |
| */ | |
| protected void removeFSWindowListener(Window w) { | |
| w.removeWindowListener(fsWindowListener); | |
| fsWindowListener = null; | |
| } | |
| } |