jdk/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java
changeset 25859 3317bb8137f4
parent 24538 25bf8153fbfe
child 28231 b608ffcaed74
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.awt;
       
    27 
       
    28 import java.awt.AWTPermission;
       
    29 import java.awt.DisplayMode;
       
    30 import java.awt.GraphicsEnvironment;
       
    31 import java.awt.GraphicsDevice;
       
    32 import java.awt.GraphicsConfiguration;
       
    33 import java.awt.Rectangle;
       
    34 import java.awt.Window;
       
    35 import java.security.AccessController;
       
    36 import java.security.PrivilegedAction;
       
    37 import java.util.ArrayList;
       
    38 import java.util.HashSet;
       
    39 import java.util.HashMap;
       
    40 
       
    41 import sun.java2d.opengl.GLXGraphicsConfig;
       
    42 import sun.java2d.xr.XRGraphicsConfig;
       
    43 import sun.java2d.loops.SurfaceType;
       
    44 
       
    45 import sun.awt.util.ThreadGroupUtils;
       
    46 
       
    47 /**
       
    48  * This is an implementation of a GraphicsDevice object for a single
       
    49  * X11 screen.
       
    50  *
       
    51  * @see GraphicsEnvironment
       
    52  * @see GraphicsConfiguration
       
    53  */
       
    54 public class X11GraphicsDevice
       
    55     extends GraphicsDevice
       
    56     implements DisplayChangedListener
       
    57 {
       
    58     int screen;
       
    59     HashMap<SurfaceType, Object> x11ProxyKeyMap = new HashMap<>();
       
    60 
       
    61     private static AWTPermission fullScreenExclusivePermission;
       
    62     private static Boolean xrandrExtSupported;
       
    63     private final Object configLock = new Object();
       
    64     private SunDisplayChanger topLevels = new SunDisplayChanger();
       
    65     private DisplayMode origDisplayMode;
       
    66     private boolean shutdownHookRegistered;
       
    67 
       
    68     public X11GraphicsDevice(int screennum) {
       
    69         this.screen = screennum;
       
    70     }
       
    71 
       
    72     /*
       
    73      * Initialize JNI field and method IDs for fields that may be
       
    74      * accessed from C.
       
    75      */
       
    76     private static native void initIDs();
       
    77 
       
    78     static {
       
    79         if (!GraphicsEnvironment.isHeadless()) {
       
    80             initIDs();
       
    81         }
       
    82     }
       
    83 
       
    84     /**
       
    85      * Returns the X11 screen of the device.
       
    86      */
       
    87     public int getScreen() {
       
    88         return screen;
       
    89     }
       
    90 
       
    91     public Object getProxyKeyFor(SurfaceType st) {
       
    92         synchronized (x11ProxyKeyMap) {
       
    93             Object o = x11ProxyKeyMap.get(st);
       
    94             if (o == null) {
       
    95                 o = new Object();
       
    96                 x11ProxyKeyMap.put(st, o);
       
    97             }
       
    98             return o;
       
    99         }
       
   100     }
       
   101 
       
   102     /**
       
   103      * Returns the X11 Display of this device.
       
   104      * This method is also in MDrawingSurfaceInfo but need it here
       
   105      * to be able to allow a GraphicsConfigTemplate to get the Display.
       
   106      */
       
   107     public native long getDisplay();
       
   108 
       
   109     /**
       
   110      * Returns the type of the graphics device.
       
   111      * @see #TYPE_RASTER_SCREEN
       
   112      * @see #TYPE_PRINTER
       
   113      * @see #TYPE_IMAGE_BUFFER
       
   114      */
       
   115     public int getType() {
       
   116         return TYPE_RASTER_SCREEN;
       
   117     }
       
   118 
       
   119     /**
       
   120      * Returns the identification string associated with this graphics
       
   121      * device.
       
   122      */
       
   123     public String getIDstring() {
       
   124         return ":0."+screen;
       
   125     }
       
   126 
       
   127 
       
   128     GraphicsConfiguration[] configs;
       
   129     GraphicsConfiguration defaultConfig;
       
   130     HashSet<Integer> doubleBufferVisuals;
       
   131 
       
   132     /**
       
   133      * Returns all of the graphics
       
   134      * configurations associated with this graphics device.
       
   135      */
       
   136     public GraphicsConfiguration[] getConfigurations() {
       
   137         if (configs == null) {
       
   138             synchronized (configLock) {
       
   139                 makeConfigurations();
       
   140             }
       
   141         }
       
   142         return configs.clone();
       
   143     }
       
   144 
       
   145     private void makeConfigurations() {
       
   146         if (configs == null) {
       
   147             int i = 1;  // Index 0 is always the default config
       
   148             int num = getNumConfigs(screen);
       
   149             GraphicsConfiguration[] ret = new GraphicsConfiguration[num];
       
   150             if (defaultConfig == null) {
       
   151                 ret [0] = getDefaultConfiguration();
       
   152             }
       
   153             else {
       
   154                 ret [0] = defaultConfig;
       
   155             }
       
   156 
       
   157             boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable();
       
   158             boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable();
       
   159 
       
   160             boolean dbeSupported = isDBESupported();
       
   161             if (dbeSupported && doubleBufferVisuals == null) {
       
   162                 doubleBufferVisuals = new HashSet<>();
       
   163                 getDoubleBufferVisuals(screen);
       
   164             }
       
   165             for ( ; i < num; i++) {
       
   166                 int visNum = getConfigVisualId(i, screen);
       
   167                 int depth = getConfigDepth (i, screen);
       
   168                 if (glxSupported) {
       
   169                     ret[i] = GLXGraphicsConfig.getConfig(this, visNum);
       
   170                 }
       
   171                 if (ret[i] == null) {
       
   172                     boolean doubleBuffer =
       
   173                         (dbeSupported &&
       
   174                          doubleBufferVisuals.contains(Integer.valueOf(visNum)));
       
   175 
       
   176                     if (xrenderSupported) {
       
   177                         ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth,                                getConfigColormap(i, screen),
       
   178                                 doubleBuffer);
       
   179                     } else {
       
   180                        ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth,
       
   181                               getConfigColormap(i, screen),
       
   182                               doubleBuffer);
       
   183                     }
       
   184                 }
       
   185             }
       
   186             configs = ret;
       
   187         }
       
   188     }
       
   189 
       
   190     /*
       
   191      * Returns the number of X11 visuals representable as an
       
   192      * X11GraphicsConfig object.
       
   193      */
       
   194     public native int getNumConfigs(int screen);
       
   195 
       
   196     /*
       
   197      * Returns the visualid for the given index of graphics configurations.
       
   198      */
       
   199     public native int getConfigVisualId (int index, int screen);
       
   200     /*
       
   201      * Returns the depth for the given index of graphics configurations.
       
   202      */
       
   203     public native int getConfigDepth (int index, int screen);
       
   204 
       
   205     /*
       
   206      * Returns the colormap for the given index of graphics configurations.
       
   207      */
       
   208     public native int getConfigColormap (int index, int screen);
       
   209 
       
   210 
       
   211     // Whether or not double-buffering extension is supported
       
   212     public static native boolean isDBESupported();
       
   213     // Callback for adding a new double buffer visual into our set
       
   214     private void addDoubleBufferVisual(int visNum) {
       
   215         doubleBufferVisuals.add(Integer.valueOf(visNum));
       
   216     }
       
   217     // Enumerates all visuals that support double buffering
       
   218     private native void getDoubleBufferVisuals(int screen);
       
   219 
       
   220     /**
       
   221      * Returns the default graphics configuration
       
   222      * associated with this graphics device.
       
   223      */
       
   224     public GraphicsConfiguration getDefaultConfiguration() {
       
   225         if (defaultConfig == null) {
       
   226             synchronized (configLock) {
       
   227                 makeDefaultConfiguration();
       
   228             }
       
   229         }
       
   230         return defaultConfig;
       
   231     }
       
   232 
       
   233     private void makeDefaultConfiguration() {
       
   234         if (defaultConfig == null) {
       
   235             int visNum = getConfigVisualId(0, screen);
       
   236             if (X11GraphicsEnvironment.isGLXAvailable()) {
       
   237                 defaultConfig = GLXGraphicsConfig.getConfig(this, visNum);
       
   238                 if (X11GraphicsEnvironment.isGLXVerbose()) {
       
   239                     if (defaultConfig != null) {
       
   240                         System.out.print("OpenGL pipeline enabled");
       
   241                     } else {
       
   242                         System.out.print("Could not enable OpenGL pipeline");
       
   243                     }
       
   244                     System.out.println(" for default config on screen " +
       
   245                                        screen);
       
   246                 }
       
   247             }
       
   248             if (defaultConfig == null) {
       
   249                 int depth = getConfigDepth(0, screen);
       
   250                 boolean doubleBuffer = false;
       
   251                 if (isDBESupported() && doubleBufferVisuals == null) {
       
   252                     doubleBufferVisuals = new HashSet<>();
       
   253                     getDoubleBufferVisuals(screen);
       
   254                     doubleBuffer =
       
   255                         doubleBufferVisuals.contains(Integer.valueOf(visNum));
       
   256                 }
       
   257 
       
   258                 if (X11GraphicsEnvironment.isXRenderAvailable()) {
       
   259                     if (X11GraphicsEnvironment.isXRenderVerbose()) {
       
   260                         System.out.println("XRender pipeline enabled");
       
   261                     }
       
   262                     defaultConfig = XRGraphicsConfig.getConfig(this, visNum,
       
   263                             depth, getConfigColormap(0, screen),
       
   264                             doubleBuffer);
       
   265                 } else {
       
   266                     defaultConfig = X11GraphicsConfig.getConfig(this, visNum,
       
   267                                         depth, getConfigColormap(0, screen),
       
   268                                         doubleBuffer);
       
   269                 }
       
   270             }
       
   271         }
       
   272     }
       
   273 
       
   274     private static native void enterFullScreenExclusive(long window);
       
   275     private static native void exitFullScreenExclusive(long window);
       
   276     private static native boolean initXrandrExtension();
       
   277     private static native DisplayMode getCurrentDisplayMode(int screen);
       
   278     private static native void enumDisplayModes(int screen,
       
   279                                                 ArrayList<DisplayMode> modes);
       
   280     private static native void configDisplayMode(int screen,
       
   281                                                  int width, int height,
       
   282                                                  int displayMode);
       
   283     private static native void resetNativeData(int screen);
       
   284 
       
   285     /**
       
   286      * Returns true only if:
       
   287      *   - the Xrandr extension is present
       
   288      *   - the necessary Xrandr functions were loaded successfully
       
   289      *   - XINERAMA is not enabled
       
   290      */
       
   291     private static synchronized boolean isXrandrExtensionSupported() {
       
   292         if (xrandrExtSupported == null) {
       
   293             xrandrExtSupported =
       
   294                 Boolean.valueOf(initXrandrExtension());
       
   295         }
       
   296         return xrandrExtSupported.booleanValue();
       
   297     }
       
   298 
       
   299     @Override
       
   300     public boolean isFullScreenSupported() {
       
   301         // REMIND: for now we will only allow fullscreen exclusive mode
       
   302         // on the primary screen; we could change this behavior slightly
       
   303         // in the future by allowing only one screen to be in fullscreen
       
   304         // exclusive mode at any given time...
       
   305         boolean fsAvailable = (screen == 0) && isXrandrExtensionSupported();
       
   306         if (fsAvailable) {
       
   307             SecurityManager security = System.getSecurityManager();
       
   308             if (security != null) {
       
   309                 if (fullScreenExclusivePermission == null) {
       
   310                     fullScreenExclusivePermission =
       
   311                         new AWTPermission("fullScreenExclusive");
       
   312                 }
       
   313                 try {
       
   314                     security.checkPermission(fullScreenExclusivePermission);
       
   315                 } catch (SecurityException e) {
       
   316                     return false;
       
   317                 }
       
   318             }
       
   319         }
       
   320         return fsAvailable;
       
   321     }
       
   322 
       
   323     @Override
       
   324     public boolean isDisplayChangeSupported() {
       
   325         return (isFullScreenSupported() && (getFullScreenWindow() != null));
       
   326     }
       
   327 
       
   328     private static void enterFullScreenExclusive(Window w) {
       
   329         X11ComponentPeer peer = (X11ComponentPeer)w.getPeer();
       
   330         if (peer != null) {
       
   331             enterFullScreenExclusive(peer.getContentWindow());
       
   332             peer.setFullScreenExclusiveModeState(true);
       
   333         }
       
   334     }
       
   335 
       
   336     private static void exitFullScreenExclusive(Window w) {
       
   337         X11ComponentPeer peer = (X11ComponentPeer)w.getPeer();
       
   338         if (peer != null) {
       
   339             peer.setFullScreenExclusiveModeState(false);
       
   340             exitFullScreenExclusive(peer.getContentWindow());
       
   341         }
       
   342     }
       
   343 
       
   344     @Override
       
   345     public synchronized void setFullScreenWindow(Window w) {
       
   346         Window old = getFullScreenWindow();
       
   347         if (w == old) {
       
   348             return;
       
   349         }
       
   350 
       
   351         boolean fsSupported = isFullScreenSupported();
       
   352         if (fsSupported && old != null) {
       
   353             // enter windowed mode (and restore original display mode)
       
   354             exitFullScreenExclusive(old);
       
   355             setDisplayMode(origDisplayMode);
       
   356         }
       
   357 
       
   358         super.setFullScreenWindow(w);
       
   359 
       
   360         if (fsSupported && w != null) {
       
   361             // save original display mode
       
   362             if (origDisplayMode == null) {
       
   363                 origDisplayMode = getDisplayMode();
       
   364             }
       
   365 
       
   366             // enter fullscreen mode
       
   367             enterFullScreenExclusive(w);
       
   368         }
       
   369     }
       
   370 
       
   371     private DisplayMode getDefaultDisplayMode() {
       
   372         GraphicsConfiguration gc = getDefaultConfiguration();
       
   373         Rectangle r = gc.getBounds();
       
   374         return new DisplayMode(r.width, r.height,
       
   375                                DisplayMode.BIT_DEPTH_MULTI,
       
   376                                DisplayMode.REFRESH_RATE_UNKNOWN);
       
   377     }
       
   378 
       
   379     @Override
       
   380     public synchronized DisplayMode getDisplayMode() {
       
   381         if (isFullScreenSupported()) {
       
   382             return getCurrentDisplayMode(screen);
       
   383         } else {
       
   384             if (origDisplayMode == null) {
       
   385                 origDisplayMode = getDefaultDisplayMode();
       
   386             }
       
   387             return origDisplayMode;
       
   388         }
       
   389     }
       
   390 
       
   391     @Override
       
   392     public synchronized DisplayMode[] getDisplayModes() {
       
   393         if (!isFullScreenSupported()) {
       
   394             return super.getDisplayModes();
       
   395         }
       
   396         ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>();
       
   397         enumDisplayModes(screen, modes);
       
   398         DisplayMode[] retArray = new DisplayMode[modes.size()];
       
   399         return modes.toArray(retArray);
       
   400     }
       
   401 
       
   402     @Override
       
   403     public synchronized void setDisplayMode(DisplayMode dm) {
       
   404         if (!isDisplayChangeSupported()) {
       
   405             super.setDisplayMode(dm);
       
   406             return;
       
   407         }
       
   408         Window w = getFullScreenWindow();
       
   409         if (w == null) {
       
   410             throw new IllegalStateException("Must be in fullscreen mode " +
       
   411                                             "in order to set display mode");
       
   412         }
       
   413         if (getDisplayMode().equals(dm)) {
       
   414             return;
       
   415         }
       
   416         if (dm == null ||
       
   417             (dm = getMatchingDisplayMode(dm)) == null)
       
   418         {
       
   419             throw new IllegalArgumentException("Invalid display mode");
       
   420         }
       
   421 
       
   422         if (!shutdownHookRegistered) {
       
   423             // register a shutdown hook so that we return to the
       
   424             // original DisplayMode when the VM exits (if the application
       
   425             // is already in the original DisplayMode at that time, this
       
   426             // hook will have no effect)
       
   427             shutdownHookRegistered = true;
       
   428             PrivilegedAction<Void> a = () -> {
       
   429                 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
       
   430                 Runnable r = () -> {
       
   431                     Window old = getFullScreenWindow();
       
   432                     if (old != null) {
       
   433                         exitFullScreenExclusive(old);
       
   434                         setDisplayMode(origDisplayMode);
       
   435                     }
       
   436                 };
       
   437                 Thread t = new Thread(rootTG, r,"Display-Change-Shutdown-Thread-"+screen);
       
   438                 t.setContextClassLoader(null);
       
   439                 Runtime.getRuntime().addShutdownHook(t);
       
   440                 return null;
       
   441             };
       
   442             AccessController.doPrivileged(a);
       
   443         }
       
   444 
       
   445         // switch to the new DisplayMode
       
   446         configDisplayMode(screen,
       
   447                           dm.getWidth(), dm.getHeight(),
       
   448                           dm.getRefreshRate());
       
   449 
       
   450         // update bounds of the fullscreen window
       
   451         w.setBounds(0, 0, dm.getWidth(), dm.getHeight());
       
   452 
       
   453         // configDisplayMode() is synchronous, so the display change will be
       
   454         // complete by the time we get here (and it is therefore safe to call
       
   455         // displayChanged() now)
       
   456         ((X11GraphicsEnvironment)
       
   457          GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged();
       
   458     }
       
   459 
       
   460     private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) {
       
   461         if (!isDisplayChangeSupported()) {
       
   462             return null;
       
   463         }
       
   464         DisplayMode[] modes = getDisplayModes();
       
   465         for (DisplayMode mode : modes) {
       
   466             if (dm.equals(mode) ||
       
   467                 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN &&
       
   468                  dm.getWidth() == mode.getWidth() &&
       
   469                  dm.getHeight() == mode.getHeight() &&
       
   470                  dm.getBitDepth() == mode.getBitDepth()))
       
   471             {
       
   472                 return mode;
       
   473             }
       
   474         }
       
   475         return null;
       
   476     }
       
   477 
       
   478     /**
       
   479      * From the DisplayChangedListener interface; called from
       
   480      * X11GraphicsEnvironment when the display mode has been changed.
       
   481      */
       
   482     public synchronized void displayChanged() {
       
   483         // On X11 the visuals do not change, and therefore we don't need
       
   484         // to reset the defaultConfig, config, doubleBufferVisuals,
       
   485         // neither do we need to reset the native data.
       
   486 
       
   487         // pass on to all top-level windows on this screen
       
   488         topLevels.notifyListeners();
       
   489     }
       
   490 
       
   491     /**
       
   492      * From the DisplayChangedListener interface; devices do not need
       
   493      * to react to this event.
       
   494      */
       
   495     public void paletteChanged() {
       
   496     }
       
   497 
       
   498     /**
       
   499      * Add a DisplayChangeListener to be notified when the display settings
       
   500      * are changed.  Typically, only top-level containers need to be added
       
   501      * to X11GraphicsDevice.
       
   502      */
       
   503     public void addDisplayChangedListener(DisplayChangedListener client) {
       
   504         topLevels.add(client);
       
   505     }
       
   506 
       
   507     /**
       
   508      * Remove a DisplayChangeListener from this X11GraphicsDevice.
       
   509      */
       
   510     public void removeDisplayChangedListener(DisplayChangedListener client) {
       
   511         topLevels.remove(client);
       
   512     }
       
   513 
       
   514     public String toString() {
       
   515         return ("X11GraphicsDevice[screen="+screen+"]");
       
   516     }
       
   517 }