Path: blob/master/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java
41152 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.GraphicsConfiguration;30import java.awt.GraphicsDevice;31import java.awt.GraphicsEnvironment;32import java.awt.Rectangle;33import java.awt.Window;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.util.ArrayList;37import java.util.HashMap;38import java.util.HashSet;3940import sun.awt.util.ThreadGroupUtils;41import sun.java2d.SunGraphicsEnvironment;42import sun.java2d.loops.SurfaceType;43import sun.java2d.opengl.GLXGraphicsConfig;44import sun.java2d.pipe.Region;45import sun.java2d.xr.XRGraphicsConfig;4647/**48* This is an implementation of a GraphicsDevice object for a single49* X11 screen.50*51* @see GraphicsEnvironment52* @see GraphicsConfiguration53*/54public final class X11GraphicsDevice extends GraphicsDevice55implements DisplayChangedListener {56/**57* X11 screen number. This identifier can become non-valid at any time58* therefore methods, which is using this id should be ready to it.59*/60private volatile int screen;61HashMap<SurfaceType, Object> x11ProxyKeyMap = new HashMap<>();6263private static AWTPermission fullScreenExclusivePermission;64private static Boolean xrandrExtSupported;65private final Object configLock = new Object();66private SunDisplayChanger topLevels = new SunDisplayChanger();67private DisplayMode origDisplayMode;68private boolean shutdownHookRegistered;69private int scale;7071public X11GraphicsDevice(int screennum) {72this.screen = screennum;73this.scale = initScaleFactor();74}7576/**77* Returns the X11 screen of the device.78*/79public int getScreen() {80return screen;81}8283public Object getProxyKeyFor(SurfaceType st) {84synchronized (x11ProxyKeyMap) {85Object o = x11ProxyKeyMap.get(st);86if (o == null) {87o = new Object();88x11ProxyKeyMap.put(st, o);89}90return o;91}92}9394/**95* Returns the X11 Display of this device.96* This method is also in MDrawingSurfaceInfo but need it here97* to be able to allow a GraphicsConfigTemplate to get the Display.98*/99public native long getDisplay();100101/**102* Returns the type of the graphics device.103* @see #TYPE_RASTER_SCREEN104* @see #TYPE_PRINTER105* @see #TYPE_IMAGE_BUFFER106*/107@Override108public int getType() {109return TYPE_RASTER_SCREEN;110}111112public int scaleUp(int x) {113return Region.clipRound(x * (double)getScaleFactor());114}115116public int scaleDown(int x) {117return Region.clipRound(x / (double)getScaleFactor());118}119120public Rectangle getBounds() {121Rectangle rect = pGetBounds(getScreen());122if (getScaleFactor() != 1) {123rect.x = scaleDown(rect.x);124rect.y = scaleDown(rect.y);125rect.width = scaleDown(rect.width);126rect.height = scaleDown(rect.height);127}128return rect;129}130131/**132* Returns the identification string associated with this graphics133* device.134*/135@Override136public String getIDstring() {137return ":0."+screen;138}139140141GraphicsConfiguration[] configs;142GraphicsConfiguration defaultConfig;143HashSet<Integer> doubleBufferVisuals;144145/**146* Returns all of the graphics147* configurations associated with this graphics device.148*/149@Override150public GraphicsConfiguration[] getConfigurations() {151if (configs == null) {152synchronized (configLock) {153makeConfigurations();154}155}156return configs.clone();157}158159private void makeConfigurations() {160if (configs == null) {161int i = 1; // Index 0 is always the default config162int num = getNumConfigs(screen);163GraphicsConfiguration[] ret = new GraphicsConfiguration[num];164if (defaultConfig == null) {165ret [0] = getDefaultConfiguration();166}167else {168ret [0] = defaultConfig;169}170171boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable();172boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable();173174boolean dbeSupported = isDBESupported();175if (dbeSupported && doubleBufferVisuals == null) {176doubleBufferVisuals = new HashSet<>();177getDoubleBufferVisuals(screen);178}179for ( ; i < num; i++) {180int visNum = getConfigVisualId(i, screen);181int depth = getConfigDepth (i, screen);182if (glxSupported) {183ret[i] = GLXGraphicsConfig.getConfig(this, visNum);184}185if (ret[i] == null) {186boolean doubleBuffer =187(dbeSupported &&188doubleBufferVisuals.contains(Integer.valueOf(visNum)));189190if (xrenderSupported) {191ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth,192getConfigColormap(i, screen), doubleBuffer);193} else {194ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth,195getConfigColormap(i, screen),196doubleBuffer);197}198}199}200configs = ret;201}202}203204/*205* Returns the number of X11 visuals representable as an206* X11GraphicsConfig object.207*/208public native int getNumConfigs(int screen);209210/*211* Returns the visualid for the given index of graphics configurations.212*/213public native int getConfigVisualId (int index, int screen);214/*215* Returns the depth for the given index of graphics configurations.216*/217private native int getConfigDepth(int index, int screen);218219/*220* Returns the colormap for the given index of graphics configurations.221*/222private native int getConfigColormap(int index, int screen);223224// Whether or not double-buffering extension is supported225static native boolean isDBESupported();226// Callback for adding a new double buffer visual into our set227private void addDoubleBufferVisual(int visNum) {228doubleBufferVisuals.add(Integer.valueOf(visNum));229}230// Enumerates all visuals that support double buffering231private native void getDoubleBufferVisuals(int screen);232233/**234* Returns the default graphics configuration235* associated with this graphics device.236*/237@Override238public GraphicsConfiguration getDefaultConfiguration() {239if (defaultConfig == null) {240synchronized (configLock) {241makeDefaultConfiguration();242}243}244return defaultConfig;245}246247private void makeDefaultConfiguration() {248if (defaultConfig == null) {249int visNum = getConfigVisualId(0, screen);250if (X11GraphicsEnvironment.isGLXAvailable()) {251defaultConfig = GLXGraphicsConfig.getConfig(this, visNum);252if (X11GraphicsEnvironment.isGLXVerbose()) {253if (defaultConfig != null) {254System.out.print("OpenGL pipeline enabled");255} else {256System.out.print("Could not enable OpenGL pipeline");257}258System.out.println(" for default config on screen " +259screen);260}261}262if (defaultConfig == null) {263int depth = getConfigDepth(0, screen);264boolean doubleBuffer = false;265if (isDBESupported() && doubleBufferVisuals == null) {266doubleBufferVisuals = new HashSet<>();267getDoubleBufferVisuals(screen);268doubleBuffer =269doubleBufferVisuals.contains(Integer.valueOf(visNum));270}271272if (X11GraphicsEnvironment.isXRenderAvailable()) {273if (X11GraphicsEnvironment.isXRenderVerbose()) {274System.out.println("XRender pipeline enabled");275}276defaultConfig = XRGraphicsConfig.getConfig(this, visNum,277depth, getConfigColormap(0, screen),278doubleBuffer);279} else {280defaultConfig = X11GraphicsConfig.getConfig(this, visNum,281depth, getConfigColormap(0, screen),282doubleBuffer);283}284}285}286}287288private static native void enterFullScreenExclusive(long window);289private static native void exitFullScreenExclusive(long window);290private static native boolean initXrandrExtension();291private static native DisplayMode getCurrentDisplayMode(int screen);292private static native void enumDisplayModes(int screen,293ArrayList<DisplayMode> modes);294private static native void configDisplayMode(int screen,295int width, int height,296int displayMode);297private static native double getNativeScaleFactor(int screen);298private native Rectangle pGetBounds(int screenNum);299300/**301* Returns true only if:302* - the Xrandr extension is present303* - the necessary Xrandr functions were loaded successfully304*/305private static synchronized boolean isXrandrExtensionSupported() {306if (xrandrExtSupported == null) {307xrandrExtSupported =308Boolean.valueOf(initXrandrExtension());309}310return xrandrExtSupported.booleanValue();311}312313@Override314public boolean isFullScreenSupported() {315boolean fsAvailable = isXrandrExtensionSupported();316if (fsAvailable) {317@SuppressWarnings("removal")318SecurityManager security = System.getSecurityManager();319if (security != null) {320if (fullScreenExclusivePermission == null) {321fullScreenExclusivePermission =322new AWTPermission("fullScreenExclusive");323}324try {325security.checkPermission(fullScreenExclusivePermission);326} catch (SecurityException e) {327return false;328}329}330}331return fsAvailable;332}333334@Override335public boolean isDisplayChangeSupported() {336return (isFullScreenSupported()337&& (getFullScreenWindow() != null)338&& !((X11GraphicsEnvironment) GraphicsEnvironment339.getLocalGraphicsEnvironment()).runningXinerama());340}341342private static void enterFullScreenExclusive(Window w) {343X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);344if (peer != null) {345enterFullScreenExclusive(peer.getWindow());346peer.setFullScreenExclusiveModeState(true);347}348}349350private static void exitFullScreenExclusive(Window w) {351X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);352if (peer != null) {353peer.setFullScreenExclusiveModeState(false);354exitFullScreenExclusive(peer.getWindow());355}356}357358@Override359public synchronized void setFullScreenWindow(Window w) {360Window old = getFullScreenWindow();361if (w == old) {362return;363}364365boolean fsSupported = isFullScreenSupported();366if (fsSupported && old != null) {367// enter windowed mode (and restore original display mode)368exitFullScreenExclusive(old);369if (isDisplayChangeSupported()) {370setDisplayMode(origDisplayMode);371}372}373374super.setFullScreenWindow(w);375376if (fsSupported && w != null) {377// save original display mode378if (origDisplayMode == null) {379origDisplayMode = getDisplayMode();380}381382// enter fullscreen mode383enterFullScreenExclusive(w);384}385}386387private DisplayMode getDefaultDisplayMode() {388GraphicsConfiguration gc = getDefaultConfiguration();389Rectangle r = gc.getBounds();390return new DisplayMode(r.width, r.height,391DisplayMode.BIT_DEPTH_MULTI,392DisplayMode.REFRESH_RATE_UNKNOWN);393}394395@Override396public synchronized DisplayMode getDisplayMode() {397if (isFullScreenSupported()) {398DisplayMode mode = getCurrentDisplayMode(screen);399if (mode == null) {400mode = getDefaultDisplayMode();401}402return mode;403} else {404if (origDisplayMode == null) {405origDisplayMode = getDefaultDisplayMode();406}407return origDisplayMode;408}409}410411@Override412public synchronized DisplayMode[] getDisplayModes() {413if (!isFullScreenSupported()414|| ((X11GraphicsEnvironment) GraphicsEnvironment415.getLocalGraphicsEnvironment()).runningXinerama()) {416// only the current mode will be returned417return super.getDisplayModes();418}419ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>();420enumDisplayModes(screen, modes);421DisplayMode[] retArray = new DisplayMode[modes.size()];422return modes.toArray(retArray);423}424425@SuppressWarnings("removal")426@Override427public synchronized void setDisplayMode(DisplayMode dm) {428if (!isDisplayChangeSupported()) {429super.setDisplayMode(dm);430return;431}432Window w = getFullScreenWindow();433if (w == null) {434throw new IllegalStateException("Must be in fullscreen mode " +435"in order to set display mode");436}437if (getDisplayMode().equals(dm)) {438return;439}440if (dm == null ||441(dm = getMatchingDisplayMode(dm)) == null)442{443throw new IllegalArgumentException("Invalid display mode");444}445446if (!shutdownHookRegistered) {447// register a shutdown hook so that we return to the448// original DisplayMode when the VM exits (if the application449// is already in the original DisplayMode at that time, this450// hook will have no effect)451shutdownHookRegistered = true;452PrivilegedAction<Void> a = () -> {453Runnable r = () -> {454Window old = getFullScreenWindow();455if (old != null) {456exitFullScreenExclusive(old);457if (isDisplayChangeSupported()) {458setDisplayMode(origDisplayMode);459}460}461};462String name = "Display-Change-Shutdown-Thread-" + screen;463Thread t = new Thread(464ThreadGroupUtils.getRootThreadGroup(), r, name, 0, false);465t.setContextClassLoader(null);466Runtime.getRuntime().addShutdownHook(t);467return null;468};469AccessController.doPrivileged(a);470}471472// switch to the new DisplayMode473configDisplayMode(screen,474dm.getWidth(), dm.getHeight(),475dm.getRefreshRate());476477// update bounds of the fullscreen window478w.setBounds(0, 0, dm.getWidth(), dm.getHeight());479480// configDisplayMode() is synchronous, so the display change will be481// complete by the time we get here (and it is therefore safe to call482// displayChanged() now)483((X11GraphicsEnvironment)484GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged();485}486487private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) {488if (!isDisplayChangeSupported()) {489return null;490}491DisplayMode[] modes = getDisplayModes();492for (DisplayMode mode : modes) {493if (dm.equals(mode) ||494(dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN &&495dm.getWidth() == mode.getWidth() &&496dm.getHeight() == mode.getHeight() &&497dm.getBitDepth() == mode.getBitDepth()))498{499return mode;500}501}502return null;503}504505/**506* From the DisplayChangedListener interface; called from507* X11GraphicsEnvironment when the display mode has been changed.508*/509@Override510public synchronized void displayChanged() {511scale = initScaleFactor();512// On X11 the visuals do not change, and therefore we don't need513// to reset the defaultConfig, config, doubleBufferVisuals,514// neither do we need to reset the native data.515516// pass on to all top-level windows on this screen517topLevels.notifyListeners();518}519520/**521* From the DisplayChangedListener interface; devices do not need522* to react to this event.523*/524@Override525public void paletteChanged() {526}527528/**529* Add a DisplayChangeListener to be notified when the display settings530* are changed. Typically, only top-level containers need to be added531* to X11GraphicsDevice.532*/533public void addDisplayChangedListener(DisplayChangedListener client) {534topLevels.add(client);535}536537public int getScaleFactor() {538return scale;539}540541public int getNativeScale() {542return (int)Math.round(getNativeScaleFactor(screen));543}544545private int initScaleFactor() {546547if (SunGraphicsEnvironment.isUIScaleEnabled()) {548549double debugScale = SunGraphicsEnvironment.getDebugScale();550551if (debugScale >= 1) {552return (int) debugScale;553}554int nativeScale = getNativeScale();555return nativeScale >= 1 ? nativeScale : 1;556}557558return 1;559}560561/**562* Remove a DisplayChangeListener from this X11GraphicsDevice.563*/564public void removeDisplayChangedListener(DisplayChangedListener client) {565topLevels.remove(client);566}567568public String toString() {569return ("X11GraphicsDevice[screen="+screen+"]");570}571572public void invalidate(X11GraphicsDevice device) {573screen = device.screen;574}575}576577578