Path: blob/master/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java
41153 views
/*1* Copyright (c) 1997, 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.EventQueue;30import java.awt.Frame;31import java.awt.GraphicsConfiguration;32import java.awt.GraphicsDevice;33import java.awt.GraphicsEnvironment;34import java.awt.Rectangle;35import java.awt.Window;36import java.awt.event.WindowAdapter;37import java.awt.event.WindowEvent;38import java.awt.event.WindowListener;39import java.awt.image.ColorModel;40import java.awt.peer.WindowPeer;41import java.util.ArrayList;42import java.util.Vector;4344import sun.awt.windows.WWindowPeer;45import sun.java2d.SunGraphicsEnvironment;46import sun.java2d.opengl.WGLGraphicsConfig;47import sun.java2d.windows.WindowsFlags;4849import static sun.awt.Win32GraphicsEnvironment.debugScaleX;50import static sun.awt.Win32GraphicsEnvironment.debugScaleY;5152/**53* This is an implementation of a GraphicsDevice object for a single54* Win32 screen.55*56* @see GraphicsEnvironment57* @see GraphicsConfiguration58*/59public class Win32GraphicsDevice extends GraphicsDevice implements60DisplayChangedListener {61int screen;62ColorModel dynamicColorModel; // updated with dev changes63ColorModel colorModel; // static for device64protected GraphicsConfiguration[] configs;65protected GraphicsConfiguration defaultConfig;6667private final String idString;68protected String descString;69// Note that we do not synchronize access to this variable - it doesn't70// really matter if a thread does an operation on graphics device which is71// about to become invalid (or already become) - we are prepared to deal72// with this on the native level.73private boolean valid;7475// keep track of top-level windows on this display76private SunDisplayChanger topLevels = new SunDisplayChanger();77// REMIND: we may disable the use of pixel formats for some accelerated78// pipelines which are mutually exclusive with opengl, for which79// pixel formats were added in the first place80protected static boolean pfDisabled;81private static AWTPermission fullScreenExclusivePermission;82// the original display mode we had before entering the fullscreen83// mode84private DisplayMode defaultDisplayMode;85// activation/deactivation listener for the full-screen window86private WindowListener fsWindowListener;8788private float scaleX;89private float scaleY;9091static {9293// 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when94// pixel format calls are made. This causes problems when a Java app95// is run as an NT service. To prevent the loading of ddraw.dll96// completely, sun.awt.nopixfmt should be set as well. Apps which use97// OpenGL w/ Java probably don't want to set this.98@SuppressWarnings("removal")99String nopixfmt = java.security.AccessController.doPrivileged(100new sun.security.action.GetPropertyAction("sun.awt.nopixfmt"));101pfDisabled = (nopixfmt != null);102initIDs();103}104105private static native void initIDs();106107native void initDevice(int screen);108native void initNativeScale(int screen);109native void setNativeScale(int screen, float scaleX, float scaleY);110native float getNativeScaleX(int screen);111native float getNativeScaleY(int screen);112113public Win32GraphicsDevice(int screennum) {114this.screen = screennum;115// we cache the strings because we want toString() and getIDstring116// to reflect the original screen number (which may change if the117// device is removed)118idString = "\\Display"+screen;119// REMIND: may be should use class name?120descString = "Win32GraphicsDevice[screen=" + screen;121valid = true;122123initDevice(screennum);124initScaleFactors();125}126127/**128* Returns the type of the graphics device.129* @see #TYPE_RASTER_SCREEN130* @see #TYPE_PRINTER131* @see #TYPE_IMAGE_BUFFER132*/133@Override134public int getType() {135return TYPE_RASTER_SCREEN;136}137138/**139* Returns the Win32 screen of the device.140*/141public int getScreen() {142return screen;143}144145public float getDefaultScaleX() {146return scaleX;147}148149public float getDefaultScaleY() {150return scaleY;151}152153private void initScaleFactors() {154if (SunGraphicsEnvironment.isUIScaleEnabled()) {155if (debugScaleX > 0 && debugScaleY > 0) {156scaleX = debugScaleX;157scaleY = debugScaleY;158setNativeScale(screen, scaleX, scaleY);159} else {160initNativeScale(screen);161scaleX = getNativeScaleX(screen);162scaleY = getNativeScaleY(screen);163}164} else {165scaleX = 1;166scaleY = 1;167}168}169170/**171* Returns whether this is a valid devicie. Device can become172* invalid as a result of device removal event.173*/174public boolean isValid() {175return valid;176}177178/**179* Called from native code when the device was removed.180*181* @param defaultScreen the current default screen182*/183protected void invalidate(int defaultScreen) {184valid = false;185screen = defaultScreen;186}187188/**189* Returns the identification string associated with this graphics190* device.191*/192@Override193public String getIDstring() {194return idString;195}196197198/**199* Returns all of the graphics200* configurations associated with this graphics device.201*/202@Override203public GraphicsConfiguration[] getConfigurations() {204if (configs==null) {205if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) {206defaultConfig = getDefaultConfiguration();207if (defaultConfig != null) {208configs = new GraphicsConfiguration[1];209configs[0] = defaultConfig;210return configs.clone();211}212}213214int max = getMaxConfigs(screen);215int defaultPixID = getDefaultPixID(screen);216Vector<GraphicsConfiguration> v = new Vector<>( max );217if (defaultPixID == 0) {218// Workaround for failing GDI calls219defaultConfig = Win32GraphicsConfig.getConfig(this,220defaultPixID);221v.addElement(defaultConfig);222}223else {224for (int i = 1; i <= max; i++) {225if (isPixFmtSupported(i, screen)) {226if (i == defaultPixID) {227defaultConfig = Win32GraphicsConfig.getConfig(228this, i);229v.addElement(defaultConfig);230}231else {232v.addElement(Win32GraphicsConfig.getConfig(233this, i));234}235}236}237}238configs = new GraphicsConfiguration[v.size()];239v.copyInto(configs);240}241return configs.clone();242}243244/**245* Returns the maximum number of graphics configurations available, or 1246* if PixelFormat calls fail or are disabled.247* This number is less than or equal to the number of graphics248* configurations supported.249*/250protected int getMaxConfigs(int screen) {251if (pfDisabled) {252return 1;253} else {254return getMaxConfigsImpl(screen);255}256}257258private native int getMaxConfigsImpl(int screen);259260/**261* Returns whether or not the PixelFormat indicated by index is262* supported. Supported PixelFormats support drawing to a Window263* (PFD_DRAW_TO_WINDOW), support GDI (PFD_SUPPORT_GDI), and in the264* case of an 8-bit format (cColorBits <= 8) uses indexed colors265* (iPixelType == PFD_TYPE_COLORINDEX).266* We use the index 0 to indicate that PixelFormat calls don't work, or267* are disabled. Do not call this function with an index of 0.268* @param index a PixelFormat index269*/270private native boolean isPixFmtSupported(int index, int screen);271272/**273* Returns the PixelFormatID of the default graphics configuration274* associated with this graphics device, or 0 if PixelFormats calls fail or275* are disabled.276*/277protected int getDefaultPixID(int screen) {278if (pfDisabled) {279return 0;280} else {281return getDefaultPixIDImpl(screen);282}283}284285/**286* Returns the default PixelFormat ID from GDI. Do not call if PixelFormats287* are disabled.288*/289private native int getDefaultPixIDImpl(int screen);290291/**292* Returns the default graphics configuration293* associated with this graphics device.294*/295@Override296public GraphicsConfiguration getDefaultConfiguration() {297if (defaultConfig == null) {298// first try to create a WGLGraphicsConfig if OGL is enabled299// REMIND: the WGL code does not yet work properly in multimon300// situations, so we will fallback on GDI if we are not on the301// default device...302if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) {303int defPixID = WGLGraphicsConfig.getDefaultPixFmt(screen);304defaultConfig = WGLGraphicsConfig.getConfig(this, defPixID);305if (WindowsFlags.isOGLVerbose()) {306if (defaultConfig != null) {307System.out.print("OpenGL pipeline enabled");308} else {309System.out.print("Could not enable OpenGL pipeline");310}311System.out.println(" for default config on screen " +312screen);313}314}315316// Fix for 4669614. Most apps are not concerned with PixelFormats,317// yet we ALWAYS used them for determining ColorModels and such.318// By passing in 0 as the PixelFormatID here, we signal that319// PixelFormats should not be used, thus avoid loading the opengl320// library. Apps concerned with PixelFormats can still use321// GraphicsConfiguration.getConfigurations().322// Note that calling native pixel format functions tends to cause323// problems between those functions (which are OpenGL-related)324// and our use of DirectX. For example, some Matrox boards will325// crash or hang calling these functions when any app is running326// in DirectX fullscreen mode. So avoiding these calls unless327// absolutely necessary is preferable.328if (defaultConfig == null) {329defaultConfig = Win32GraphicsConfig.getConfig(this, 0);330}331}332return defaultConfig;333}334335@Override336public String toString() {337return valid ? descString + "]" : descString + ", removed]";338}339340/**341* Returns true if this is the default GraphicsDevice for the342* GraphicsEnvironment.343*/344private boolean isDefaultDevice() {345return (this ==346GraphicsEnvironment.347getLocalGraphicsEnvironment().getDefaultScreenDevice());348}349350private static boolean isFSExclusiveModeAllowed() {351@SuppressWarnings("removal")352SecurityManager security = System.getSecurityManager();353if (security != null) {354if (fullScreenExclusivePermission == null) {355fullScreenExclusivePermission =356new AWTPermission("fullScreenExclusive");357}358try {359security.checkPermission(fullScreenExclusivePermission);360} catch (SecurityException e) {361return false;362}363}364return true;365}366367/**368* returns true unless we're not allowed to use fullscreen mode.369*/370@Override371public boolean isFullScreenSupported() {372return isFSExclusiveModeAllowed();373}374375@Override376public synchronized void setFullScreenWindow(Window w) {377Window old = getFullScreenWindow();378if (w == old) {379return;380}381if (!isFullScreenSupported()) {382super.setFullScreenWindow(w);383return;384}385386// Enter windowed mode.387if (old != null) {388// restore the original display mode389if (defaultDisplayMode != null) {390setDisplayMode(defaultDisplayMode);391// we set the default display mode to null here392// because the default mode could change during393// the life of the application (user can change it through394// the desktop properties dialog, for example), so395// we need to record it every time prior to396// entering the fullscreen mode.397defaultDisplayMode = null;398}399WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(old);400if (peer != null) {401peer.setFullScreenExclusiveModeState(false);402// we used to destroy the buffers on exiting fs mode, this403// is no longer needed since fs change will cause a surface404// data replacement405synchronized(peer) {406exitFullScreenExclusive(screen, peer);407}408}409removeFSWindowListener(old);410}411super.setFullScreenWindow(w);412if (w != null) {413// always record the default display mode prior to going414// fullscreen415defaultDisplayMode = getDisplayMode();416addFSWindowListener(w);417// Enter full screen exclusive mode.418WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);419if (peer != null) {420synchronized(peer) {421enterFullScreenExclusive(screen, peer);422// Note: removed replaceSurfaceData() call because423// changing the window size or making it visible424// will cause this anyway, and both of these events happen425// as part of switching into fullscreen mode.426}427peer.setFullScreenExclusiveModeState(true);428}429430// fix for 4868278431peer.updateGC();432}433}434435// Entering and exiting full-screen mode are done within a436// tree-lock and should never lock on any resources which are437// required by other threads which may have them and may require438// the tree-lock.439// REMIND: in the future these methods may need to become protected so that440// subclasses could override them and use appropriate api other than GDI441// for implementing these functions.442protected native void enterFullScreenExclusive(int screen, WindowPeer w);443protected native void exitFullScreenExclusive(int screen, WindowPeer w);444445@Override446public boolean isDisplayChangeSupported() {447return (isFullScreenSupported() && getFullScreenWindow() != null);448}449450@Override451public synchronized void setDisplayMode(DisplayMode dm) {452if (!isDisplayChangeSupported()) {453super.setDisplayMode(dm);454return;455}456if (dm == null || (dm = getMatchingDisplayMode(dm)) == null) {457throw new IllegalArgumentException("Invalid display mode");458}459if (getDisplayMode().equals(dm)) {460return;461}462Window w = getFullScreenWindow();463if (w != null) {464WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);465configDisplayMode(screen, peer, dm.getWidth(), dm.getHeight(),466dm.getBitDepth(), dm.getRefreshRate());467// resize the fullscreen window to the dimensions of the new468// display mode469Rectangle screenBounds = getDefaultConfiguration().getBounds();470w.setBounds(screenBounds.x, screenBounds.y,471screenBounds.width, screenBounds.height);472// Note: no call to replaceSurfaceData is required here since473// replacement will be caused by an upcoming display change event474} else {475throw new IllegalStateException("Must be in fullscreen mode " +476"in order to set display mode");477}478}479480protected native DisplayMode getCurrentDisplayMode(int screen);481protected native void configDisplayMode(int screen, WindowPeer w, int width,482int height, int bitDepth,483int refreshRate);484protected native void enumDisplayModes(int screen, ArrayList<DisplayMode> modes);485486@Override487public synchronized DisplayMode getDisplayMode() {488DisplayMode res = getCurrentDisplayMode(screen);489return res;490}491492@Override493public synchronized DisplayMode[] getDisplayModes() {494ArrayList<DisplayMode> modes = new ArrayList<>();495enumDisplayModes(screen, modes);496int listSize = modes.size();497DisplayMode[] retArray = new DisplayMode[listSize];498for (int i = 0; i < listSize; i++) {499retArray[i] = modes.get(i);500}501return retArray;502}503504protected synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) {505if (!isDisplayChangeSupported()) {506return null;507}508DisplayMode[] modes = getDisplayModes();509for (DisplayMode mode : modes) {510if (dm.equals(mode) ||511(dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN &&512dm.getWidth() == mode.getWidth() &&513dm.getHeight() == mode.getHeight() &&514dm.getBitDepth() == mode.getBitDepth()))515{516return mode;517}518}519return null;520}521522/*523* From the DisplayChangeListener interface.524* Called from Win32GraphicsEnvironment when the display settings have525* changed.526*/527@Override528public void displayChanged() {529dynamicColorModel = null;530defaultConfig = null;531configs = null;532initScaleFactors();533// pass on to all top-level windows on this display534topLevels.notifyListeners();535}536537/**538* Part of the DisplayChangedListener interface: devices539* do not need to react to this event540*/541@Override542public void paletteChanged() {543}544545/*546* Add a DisplayChangeListener to be notified when the display settings547* are changed. Typically, only top-level containers need to be added548* to Win32GraphicsDevice.549*/550public void addDisplayChangedListener(DisplayChangedListener client) {551topLevels.add(client);552}553554/*555* Remove a DisplayChangeListener from this Win32GraphicsDevice556*/557public void removeDisplayChangedListener(DisplayChangedListener client) {558topLevels.remove(client);559}560561/**562* Creates and returns the color model associated with this device563*/564private native ColorModel makeColorModel (int screen,565boolean dynamic);566567/**568* Returns a dynamic ColorModel which is updated when there569* are any changes (e.g., palette changes) in the device570*/571public ColorModel getDynamicColorModel() {572if (dynamicColorModel == null) {573dynamicColorModel = makeColorModel(screen, true);574}575return dynamicColorModel;576}577578/**579* Returns the non-dynamic ColorModel associated with this device580*/581public ColorModel getColorModel() {582if (colorModel == null) {583colorModel = makeColorModel(screen, false);584}585return colorModel;586}587588/**589* WindowAdapter class responsible for de/iconifying full-screen window590* of this device.591*592* The listener restores the default display mode when window is iconified593* and sets it back to the one set by the user on de-iconification.594*/595private static class Win32FSWindowAdapter extends WindowAdapter {596private Win32GraphicsDevice device;597private DisplayMode dm;598599Win32FSWindowAdapter(Win32GraphicsDevice device) {600this.device = device;601}602603private void setFSWindowsState(Window other, int state) {604GraphicsDevice[] gds =605GraphicsEnvironment.getLocalGraphicsEnvironment().606getScreenDevices();607// check if the de/activation was caused by other608// fs window and ignore the event if that's the case609if (other != null) {610for (GraphicsDevice gd : gds) {611if (other == gd.getFullScreenWindow()) {612return;613}614}615}616// otherwise apply state to all fullscreen windows617for (GraphicsDevice gd : gds) {618Window fsw = gd.getFullScreenWindow();619if (fsw instanceof Frame) {620((Frame)fsw).setExtendedState(state);621}622}623}624625@Override626public void windowDeactivated(WindowEvent e) {627setFSWindowsState(e.getOppositeWindow(), Frame.ICONIFIED);628}629630@Override631public void windowActivated(WindowEvent e) {632setFSWindowsState(e.getOppositeWindow(), Frame.NORMAL);633}634635@Override636public void windowIconified(WindowEvent e) {637// restore the default display mode for this device638DisplayMode ddm = device.defaultDisplayMode;639if (ddm != null) {640dm = device.getDisplayMode();641device.setDisplayMode(ddm);642}643}644645@Override646public void windowDeiconified(WindowEvent e) {647// restore the user-set display mode for this device648if (dm != null) {649device.setDisplayMode(dm);650dm = null;651}652}653}654655/**656* Adds a WindowListener to be used as657* activation/deactivation listener for the current full-screen window.658*659* @param w full-screen window660*/661protected void addFSWindowListener(final Window w) {662// Note: even though we create a listener for Window instances of663// fs windows they will not receive window events.664fsWindowListener = new Win32FSWindowAdapter(this);665666// Fix for 6709453. Using invokeLater to avoid listening667// for the events already posted to the queue.668EventQueue.invokeLater(new Runnable() {669@Override670public void run() {671w.addWindowListener(fsWindowListener);672}673});674}675676/**677* Removes the fs window listener.678*679* @param w full-screen window680*/681protected void removeFSWindowListener(Window w) {682w.removeWindowListener(fsWindowListener);683fsWindowListener = null;684}685}686687688