/*
 * Copyright (c) 1997, 2016, 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 Also:
/** * 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 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.
/** * 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.
/** * 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 Also:
/** * 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.
/** * 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.
/** * 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.
/** * 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 double getNativeScaleFactor(int screen);
Returns true only if: - the Xrandr extension is present - the necessary Xrandr functions were loaded successfully
/** * 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.
/** * From the DisplayChangedListener interface; called from * X11GraphicsEnvironment when the display mode has been changed. */
public synchronized void displayChanged() { scale = initScaleFactor(); // 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.
/** * 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.
/** * 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; } public int getNativeScale() { isXrandrExtensionSupported(); return (int)Math.round(getNativeScaleFactor(screen)); } private int initScaleFactor() { if (SunGraphicsEnvironment.isUIScaleEnabled()) { double debugScale = SunGraphicsEnvironment.getDebugScale(); if (debugScale >= 1) { return (int) debugScale; } int nativeScale = getNativeScale(); return nativeScale >= 1 ? nativeScale : 1; } return 1; }
Remove a DisplayChangeListener from this X11GraphicsDevice.
/** * Remove a DisplayChangeListener from this X11GraphicsDevice. */
public void removeDisplayChangedListener(DisplayChangedListener client) { topLevels.remove(client); } public String toString() { return ("X11GraphicsDevice[screen="+screen+"]"); } }