| /* |
| * Copyright (c) 1997, 2015, 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.AWTPermission; |
| import java.awt.DisplayMode; |
| import java.awt.GraphicsEnvironment; |
| import java.awt.GraphicsDevice; |
| import java.awt.GraphicsConfiguration; |
| import java.awt.Rectangle; |
| import java.awt.Window; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.HashMap; |
| |
| import sun.java2d.opengl.GLXGraphicsConfig; |
| import sun.java2d.xr.XRGraphicsConfig; |
| import sun.java2d.loops.SurfaceType; |
| |
| import sun.awt.util.ThreadGroupUtils; |
| import sun.java2d.SunGraphicsEnvironment; |
| |
| /** |
| * This is an implementation of a GraphicsDevice object for a single |
| * X11 screen. |
| * |
| * @see GraphicsEnvironment |
| * @see GraphicsConfiguration |
| */ |
| public final class X11GraphicsDevice extends GraphicsDevice |
| implements DisplayChangedListener { |
| int screen; |
| HashMap<SurfaceType, Object> x11ProxyKeyMap = new HashMap<>(); |
| |
| private static AWTPermission fullScreenExclusivePermission; |
| private static Boolean xrandrExtSupported; |
| private final Object configLock = new Object(); |
| private SunDisplayChanger topLevels = new SunDisplayChanger(); |
| private DisplayMode origDisplayMode; |
| private boolean shutdownHookRegistered; |
| private final int scale; |
| |
| public X11GraphicsDevice(int screennum) { |
| this.screen = screennum; |
| this.scale = initScaleFactor(); |
| } |
| |
| /* |
| * Initialize JNI field and method IDs for fields that may be |
| * accessed from C. |
| */ |
| private static native void initIDs(); |
| |
| static { |
| if (!GraphicsEnvironment.isHeadless()) { |
| initIDs(); |
| } |
| } |
| |
| /** |
| * Returns the X11 screen of the device. |
| */ |
| public int getScreen() { |
| return screen; |
| } |
| |
| public Object getProxyKeyFor(SurfaceType st) { |
| synchronized (x11ProxyKeyMap) { |
| Object o = x11ProxyKeyMap.get(st); |
| if (o == null) { |
| o = new Object(); |
| x11ProxyKeyMap.put(st, o); |
| } |
| return o; |
| } |
| } |
| |
| /** |
| * Returns the X11 Display of this device. |
| * This method is also in MDrawingSurfaceInfo but need it here |
| * to be able to allow a GraphicsConfigTemplate to get the Display. |
| */ |
| public native long getDisplay(); |
| |
| /** |
| * Returns the type of the graphics device. |
| * @see #TYPE_RASTER_SCREEN |
| * @see #TYPE_PRINTER |
| * @see #TYPE_IMAGE_BUFFER |
| */ |
| public int getType() { |
| return TYPE_RASTER_SCREEN; |
| } |
| |
| /** |
| * Returns the identification string associated with this graphics |
| * device. |
| */ |
| public String getIDstring() { |
| return ":0."+screen; |
| } |
| |
| |
| GraphicsConfiguration[] configs; |
| GraphicsConfiguration defaultConfig; |
| HashSet<Integer> doubleBufferVisuals; |
| |
| /** |
| * Returns all of the graphics |
| * configurations associated with this graphics device. |
| */ |
| public GraphicsConfiguration[] getConfigurations() { |
| if (configs == null) { |
| synchronized (configLock) { |
| makeConfigurations(); |
| } |
| } |
| return configs.clone(); |
| } |
| |
| private void makeConfigurations() { |
| if (configs == null) { |
| int i = 1; // Index 0 is always the default config |
| int num = getNumConfigs(screen); |
| GraphicsConfiguration[] ret = new GraphicsConfiguration[num]; |
| if (defaultConfig == null) { |
| ret [0] = getDefaultConfiguration(); |
| } |
| else { |
| ret [0] = defaultConfig; |
| } |
| |
| boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable(); |
| boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable(); |
| |
| boolean dbeSupported = isDBESupported(); |
| if (dbeSupported && doubleBufferVisuals == null) { |
| doubleBufferVisuals = new HashSet<>(); |
| getDoubleBufferVisuals(screen); |
| } |
| for ( ; i < num; i++) { |
| int visNum = getConfigVisualId(i, screen); |
| int depth = getConfigDepth (i, screen); |
| if (glxSupported) { |
| ret[i] = GLXGraphicsConfig.getConfig(this, visNum); |
| } |
| if (ret[i] == null) { |
| boolean doubleBuffer = |
| (dbeSupported && |
| doubleBufferVisuals.contains(Integer.valueOf(visNum))); |
| |
| if (xrenderSupported) { |
| ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth, getConfigColormap(i, screen), |
| doubleBuffer); |
| } else { |
| ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth, |
| getConfigColormap(i, screen), |
| doubleBuffer); |
| } |
| } |
| } |
| configs = ret; |
| } |
| } |
| |
| /* |
| * Returns the number of X11 visuals representable as an |
| * X11GraphicsConfig object. |
| */ |
| public native int getNumConfigs(int screen); |
| |
| /* |
| * Returns the visualid for the given index of graphics configurations. |
| */ |
| public native int getConfigVisualId (int index, int screen); |
| /* |
| * Returns the depth for the given index of graphics configurations. |
| */ |
| private native int getConfigDepth(int index, int screen); |
| |
| /* |
| * Returns the colormap for the given index of graphics configurations. |
| */ |
| private native int getConfigColormap(int index, int screen); |
| |
| // Whether or not double-buffering extension is supported |
| static native boolean isDBESupported(); |
| // Callback for adding a new double buffer visual into our set |
| private void addDoubleBufferVisual(int visNum) { |
| doubleBufferVisuals.add(Integer.valueOf(visNum)); |
| } |
| // Enumerates all visuals that support double buffering |
| private native void getDoubleBufferVisuals(int screen); |
| |
| /** |
| * Returns the default graphics configuration |
| * associated with this graphics device. |
| */ |
| public GraphicsConfiguration getDefaultConfiguration() { |
| if (defaultConfig == null) { |
| synchronized (configLock) { |
| makeDefaultConfiguration(); |
| } |
| } |
| return defaultConfig; |
| } |
| |
| private void makeDefaultConfiguration() { |
| if (defaultConfig == null) { |
| int visNum = getConfigVisualId(0, screen); |
| if (X11GraphicsEnvironment.isGLXAvailable()) { |
| defaultConfig = GLXGraphicsConfig.getConfig(this, visNum); |
| if (X11GraphicsEnvironment.isGLXVerbose()) { |
| 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); |
| } |
| } |
| if (defaultConfig == null) { |
| int depth = getConfigDepth(0, screen); |
| boolean doubleBuffer = false; |
| if (isDBESupported() && doubleBufferVisuals == null) { |
| doubleBufferVisuals = new HashSet<>(); |
| getDoubleBufferVisuals(screen); |
| doubleBuffer = |
| doubleBufferVisuals.contains(Integer.valueOf(visNum)); |
| } |
| |
| if (X11GraphicsEnvironment.isXRenderAvailable()) { |
| if (X11GraphicsEnvironment.isXRenderVerbose()) { |
| System.out.println("XRender pipeline enabled"); |
| } |
| defaultConfig = XRGraphicsConfig.getConfig(this, visNum, |
| depth, getConfigColormap(0, screen), |
| doubleBuffer); |
| } else { |
| defaultConfig = X11GraphicsConfig.getConfig(this, visNum, |
| depth, getConfigColormap(0, screen), |
| doubleBuffer); |
| } |
| } |
| } |
| } |
| |
| private static native void enterFullScreenExclusive(long window); |
| private static native void exitFullScreenExclusive(long window); |
| private static native boolean initXrandrExtension(); |
| private static native DisplayMode getCurrentDisplayMode(int screen); |
| private static native void enumDisplayModes(int screen, |
| ArrayList<DisplayMode> modes); |
| private static native void configDisplayMode(int screen, |
| int width, int height, |
| int displayMode); |
| private static native void resetNativeData(int screen); |
| private static native int getNativeScaleFactor(int screen); |
| |
| /** |
| * Returns true only if: |
| * - the Xrandr extension is present |
| * - the necessary Xrandr functions were loaded successfully |
| */ |
| private static synchronized boolean isXrandrExtensionSupported() { |
| if (xrandrExtSupported == null) { |
| xrandrExtSupported = |
| Boolean.valueOf(initXrandrExtension()); |
| } |
| return xrandrExtSupported.booleanValue(); |
| } |
| |
| @Override |
| public boolean isFullScreenSupported() { |
| boolean fsAvailable = isXrandrExtensionSupported(); |
| if (fsAvailable) { |
| SecurityManager security = System.getSecurityManager(); |
| if (security != null) { |
| if (fullScreenExclusivePermission == null) { |
| fullScreenExclusivePermission = |
| new AWTPermission("fullScreenExclusive"); |
| } |
| try { |
| security.checkPermission(fullScreenExclusivePermission); |
| } catch (SecurityException e) { |
| return false; |
| } |
| } |
| } |
| return fsAvailable; |
| } |
| |
| @Override |
| public boolean isDisplayChangeSupported() { |
| return (isFullScreenSupported() |
| && (getFullScreenWindow() != null) |
| && !((X11GraphicsEnvironment) GraphicsEnvironment |
| .getLocalGraphicsEnvironment()).runningXinerama()); |
| } |
| |
| private static void enterFullScreenExclusive(Window w) { |
| X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); |
| if (peer != null) { |
| enterFullScreenExclusive(peer.getWindow()); |
| peer.setFullScreenExclusiveModeState(true); |
| } |
| } |
| |
| private static void exitFullScreenExclusive(Window w) { |
| X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); |
| if (peer != null) { |
| peer.setFullScreenExclusiveModeState(false); |
| exitFullScreenExclusive(peer.getWindow()); |
| } |
| } |
| |
| @Override |
| public synchronized void setFullScreenWindow(Window w) { |
| Window old = getFullScreenWindow(); |
| if (w == old) { |
| return; |
| } |
| |
| boolean fsSupported = isFullScreenSupported(); |
| if (fsSupported && old != null) { |
| // enter windowed mode (and restore original display mode) |
| exitFullScreenExclusive(old); |
| if (isDisplayChangeSupported()) { |
| setDisplayMode(origDisplayMode); |
| } |
| } |
| |
| super.setFullScreenWindow(w); |
| |
| if (fsSupported && w != null) { |
| // save original display mode |
| if (origDisplayMode == null) { |
| origDisplayMode = getDisplayMode(); |
| } |
| |
| // enter fullscreen mode |
| enterFullScreenExclusive(w); |
| } |
| } |
| |
| private DisplayMode getDefaultDisplayMode() { |
| GraphicsConfiguration gc = getDefaultConfiguration(); |
| Rectangle r = gc.getBounds(); |
| return new DisplayMode(r.width, r.height, |
| DisplayMode.BIT_DEPTH_MULTI, |
| DisplayMode.REFRESH_RATE_UNKNOWN); |
| } |
| |
| @Override |
| public synchronized DisplayMode getDisplayMode() { |
| if (isFullScreenSupported()) { |
| DisplayMode mode = getCurrentDisplayMode(screen); |
| if (mode == null) { |
| mode = getDefaultDisplayMode(); |
| } |
| return mode; |
| } else { |
| if (origDisplayMode == null) { |
| origDisplayMode = getDefaultDisplayMode(); |
| } |
| return origDisplayMode; |
| } |
| } |
| |
| @Override |
| public synchronized DisplayMode[] getDisplayModes() { |
| if (!isFullScreenSupported()) { |
| return super.getDisplayModes(); |
| } |
| ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>(); |
| enumDisplayModes(screen, modes); |
| DisplayMode[] retArray = new DisplayMode[modes.size()]; |
| return modes.toArray(retArray); |
| } |
| |
| @Override |
| public synchronized void setDisplayMode(DisplayMode dm) { |
| if (!isDisplayChangeSupported()) { |
| super.setDisplayMode(dm); |
| return; |
| } |
| Window w = getFullScreenWindow(); |
| if (w == null) { |
| throw new IllegalStateException("Must be in fullscreen mode " + |
| "in order to set display mode"); |
| } |
| if (getDisplayMode().equals(dm)) { |
| return; |
| } |
| if (dm == null || |
| (dm = getMatchingDisplayMode(dm)) == null) |
| { |
| throw new IllegalArgumentException("Invalid display mode"); |
| } |
| |
| if (!shutdownHookRegistered) { |
| // register a shutdown hook so that we return to the |
| // original DisplayMode when the VM exits (if the application |
| // is already in the original DisplayMode at that time, this |
| // hook will have no effect) |
| shutdownHookRegistered = true; |
| PrivilegedAction<Void> a = () -> { |
| Runnable r = () -> { |
| Window old = getFullScreenWindow(); |
| if (old != null) { |
| exitFullScreenExclusive(old); |
| if (isDisplayChangeSupported()) { |
| setDisplayMode(origDisplayMode); |
| } |
| } |
| }; |
| String name = "Display-Change-Shutdown-Thread-" + screen; |
| Thread t = new Thread( |
| ThreadGroupUtils.getRootThreadGroup(), r, name, 0, false); |
| t.setContextClassLoader(null); |
| Runtime.getRuntime().addShutdownHook(t); |
| return null; |
| }; |
| AccessController.doPrivileged(a); |
| } |
| |
| // switch to the new DisplayMode |
| configDisplayMode(screen, |
| dm.getWidth(), dm.getHeight(), |
| dm.getRefreshRate()); |
| |
| // update bounds of the fullscreen window |
| w.setBounds(0, 0, dm.getWidth(), dm.getHeight()); |
| |
| // configDisplayMode() is synchronous, so the display change will be |
| // complete by the time we get here (and it is therefore safe to call |
| // displayChanged() now) |
| ((X11GraphicsEnvironment) |
| GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged(); |
| } |
| |
| private 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 DisplayChangedListener interface; called from |
| * X11GraphicsEnvironment when the display mode has been changed. |
| */ |
| public synchronized void displayChanged() { |
| // On X11 the visuals do not change, and therefore we don't need |
| // to reset the defaultConfig, config, doubleBufferVisuals, |
| // neither do we need to reset the native data. |
| |
| // pass on to all top-level windows on this screen |
| topLevels.notifyListeners(); |
| } |
| |
| /** |
| * From the DisplayChangedListener interface; devices do not need |
| * to react to this event. |
| */ |
| 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 X11GraphicsDevice. |
| */ |
| public void addDisplayChangedListener(DisplayChangedListener client) { |
| topLevels.add(client); |
| } |
| |
| public int getScaleFactor() { |
| return scale; |
| } |
| |
| private int initScaleFactor() { |
| |
| if (SunGraphicsEnvironment.isUIScaleEnabled()) { |
| |
| double debugScale = SunGraphicsEnvironment.getDebugScale(); |
| |
| if (debugScale >= 1) { |
| return (int) debugScale; |
| } |
| |
| int nativeScale = getNativeScaleFactor(screen); |
| return nativeScale >= 1 ? nativeScale : 1; |
| } |
| |
| return 1; |
| } |
| |
| /** |
| * Remove a DisplayChangeListener from this X11GraphicsDevice. |
| */ |
| public void removeDisplayChangedListener(DisplayChangedListener client) { |
| topLevels.remove(client); |
| } |
| |
| public String toString() { |
| return ("X11GraphicsDevice[screen="+screen+"]"); |
| } |
| } |