1 /*
   2  * Copyright (c) 1997, 2015, 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.AWTError;
  29 import java.awt.GraphicsDevice;
  30 import java.awt.Point;
  31 import java.awt.Rectangle;
  32 import java.net.InetAddress;
  33 import java.net.NetworkInterface;
  34 import java.net.SocketException;
  35 import java.net.UnknownHostException;
  36 
  37 import java.util.*;
  38 
  39 import sun.java2d.SunGraphicsEnvironment;
  40 import sun.java2d.SurfaceManagerFactory;
  41 import sun.java2d.UnixSurfaceManagerFactory;
  42 import sun.util.logging.PlatformLogger;
  43 import sun.java2d.xr.XRSurfaceData;
  44 
  45 /**
  46  * This is an implementation of a GraphicsEnvironment object for the
  47  * default local GraphicsEnvironment used by the Java Runtime Environment
  48  * for X11 environments.
  49  *
  50  * @see GraphicsDevice
  51  * @see java.awt.GraphicsConfiguration
  52  */
  53 public final class X11GraphicsEnvironment extends SunGraphicsEnvironment {
  54 
  55     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11GraphicsEnvironment");
  56     private static final PlatformLogger screenLog = PlatformLogger.getLogger("sun.awt.screen.X11GraphicsEnvironment");
  57 
  58     private static Boolean xinerState;
  59 
  60     static {
  61         java.security.AccessController.doPrivileged(
  62                           new java.security.PrivilegedAction<Object>() {
  63             public Object run() {
  64                 System.loadLibrary("awt");
  65 
  66                 /*
  67                  * Note: The MToolkit object depends on the static initializer
  68                  * of X11GraphicsEnvironment to initialize the connection to
  69                  * the X11 server.
  70                  */
  71                 if (!isHeadless()) {
  72                     // first check the OGL system property
  73                     boolean glxRequested = false;
  74                     String prop = System.getProperty("sun.java2d.opengl");
  75                     if (prop != null) {
  76                         if (prop.equals("true") || prop.equals("t")) {
  77                             glxRequested = true;
  78                         } else if (prop.equals("True") || prop.equals("T")) {
  79                             glxRequested = true;
  80                             glxVerbose = true;
  81                         }
  82                     }
  83 
  84                     // Now check for XRender system property
  85                     boolean xRenderRequested = true;
  86                     boolean xRenderIgnoreLinuxVersion = false;
  87                     String xProp = System.getProperty("sun.java2d.xrender");
  88                         if (xProp != null) {
  89                         if (xProp.equals("false") || xProp.equals("f")) {
  90                             xRenderRequested = false;
  91                         } else if (xProp.equals("True") || xProp.equals("T")) {
  92                             xRenderRequested = true;
  93                             xRenderVerbose = true;
  94                         }
  95 
  96                         if(xProp.equalsIgnoreCase("t") || xProp.equalsIgnoreCase("true")) {
  97                             xRenderIgnoreLinuxVersion = true;
  98                         }
  99                     }
 100 
 101                     // initialize the X11 display connection
 102                     initDisplay(glxRequested);
 103 
 104                     // only attempt to initialize GLX if it was requested
 105                     if (glxRequested) {
 106                         glxAvailable = initGLX();
 107                         if (glxVerbose && !glxAvailable) {
 108                             System.out.println(
 109                                 "Could not enable OpenGL " +
 110                                 "pipeline (GLX 1.3 not available)");
 111                         }
 112                     }
 113 
 114                     // only attempt to initialize Xrender if it was requested
 115                     if (xRenderRequested) {
 116                         xRenderAvailable = initXRender(xRenderVerbose, xRenderIgnoreLinuxVersion);
 117                         if (xRenderVerbose && !xRenderAvailable) {
 118                             System.out.println(
 119                                          "Could not enable XRender pipeline");
 120                         }
 121                     }
 122 
 123                     if (xRenderAvailable) {
 124                         XRSurfaceData.initXRSurfaceData();
 125                     }
 126                 }
 127 
 128                 return null;
 129             }
 130          });
 131 
 132         // Install the correct surface manager factory.
 133         SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
 134 
 135     }
 136 
 137 
 138     private static boolean glxAvailable;
 139     private static boolean glxVerbose;
 140 
 141     private static native boolean initGLX();
 142 
 143     public static boolean isGLXAvailable() {
 144         return glxAvailable;
 145     }
 146 
 147     public static boolean isGLXVerbose() {
 148         return glxVerbose;
 149     }
 150 
 151     private static boolean xRenderVerbose;
 152     private static boolean xRenderAvailable;
 153 
 154     private static native boolean initXRender(boolean verbose, boolean ignoreLinuxVersion);
 155     public static boolean isXRenderAvailable() {
 156         return xRenderAvailable;
 157     }
 158 
 159     public static boolean isXRenderVerbose() {
 160         return xRenderVerbose;
 161     }
 162 
 163     /**
 164      * Checks if Shared Memory extension can be used.
 165      * Returns:
 166      *   -1 if server doesn't support MITShm
 167      *    1 if server supports it and it can be used
 168      *    0 otherwise
 169      */
 170     private static native int checkShmExt();
 171 
 172     private static  native String getDisplayString();
 173     private Boolean isDisplayLocal;
 174 
 175     /**
 176      * This should only be called from the static initializer, so no need for
 177      * the synchronized keyword.
 178      */
 179     private static native void initDisplay(boolean glxRequested);
 180 
 181     public X11GraphicsEnvironment() {
 182     }
 183 
 184     protected native int getNumScreens();
 185 
 186     protected GraphicsDevice makeScreenDevice(int screennum) {
 187         return new X11GraphicsDevice(screennum);
 188     }
 189 
 190     private native int getDefaultScreenNum();
 191     /**
 192      * Returns the default screen graphics device.
 193      */
 194     public GraphicsDevice getDefaultScreenDevice() {
 195         GraphicsDevice[] screens = getScreenDevices();
 196         if (screens.length == 0) {
 197             throw new AWTError("no screen devices");
 198         }
 199         int index = getDefaultScreenNum();
 200         return screens[0 < index && index < screens.length ? index : 0];
 201     }
 202 
 203     public boolean isDisplayLocal() {
 204         if (isDisplayLocal == null) {
 205             SunToolkit.awtLock();
 206             try {
 207                 if (isDisplayLocal == null) {
 208                     isDisplayLocal = Boolean.valueOf(_isDisplayLocal());
 209                 }
 210             } finally {
 211                 SunToolkit.awtUnlock();
 212             }
 213         }
 214         return isDisplayLocal.booleanValue();
 215     }
 216 
 217     private static boolean _isDisplayLocal() {
 218         if (isHeadless()) {
 219             return true;
 220         }
 221 
 222         String isRemote = java.security.AccessController.doPrivileged(
 223             new sun.security.action.GetPropertyAction("sun.java2d.remote"));
 224         if (isRemote != null) {
 225             return isRemote.equals("false");
 226         }
 227 
 228         int shm = checkShmExt();
 229         if (shm != -1) {
 230             return (shm == 1);
 231         }
 232 
 233         // If XServer doesn't support ShMem extension,
 234         // try the other way
 235 
 236         String display = getDisplayString();
 237         int ind = display.indexOf(':');
 238         final String hostName = display.substring(0, ind);
 239         if (ind <= 0) {
 240             // ':0' case
 241             return true;
 242         }
 243 
 244         Boolean result = java.security.AccessController.doPrivileged(
 245             new java.security.PrivilegedAction<Boolean>() {
 246             public Boolean run() {
 247                 InetAddress remAddr[] = null;
 248                 Enumeration<InetAddress> locals = null;
 249                 Enumeration<NetworkInterface> interfaces = null;
 250                 try {
 251                     interfaces = NetworkInterface.getNetworkInterfaces();
 252                     remAddr = InetAddress.getAllByName(hostName);
 253                     if (remAddr == null) {
 254                         return Boolean.FALSE;
 255                     }
 256                 } catch (UnknownHostException e) {
 257                     System.err.println("Unknown host: " + hostName);
 258                     return Boolean.FALSE;
 259                 } catch (SocketException e1) {
 260                     System.err.println(e1.getMessage());
 261                     return Boolean.FALSE;
 262                 }
 263 
 264                 for (; interfaces.hasMoreElements();) {
 265                     locals = interfaces.nextElement().getInetAddresses();
 266                     for (; locals.hasMoreElements();) {
 267                         final InetAddress localAddr = locals.nextElement();
 268                         for (int i = 0; i < remAddr.length; i++) {
 269                             if (localAddr.equals(remAddr[i])) {
 270                                 return Boolean.TRUE;
 271                             }
 272                         }
 273                     }
 274                 }
 275                 return Boolean.FALSE;
 276             }});
 277         return result.booleanValue();
 278     }
 279 
 280 
 281 
 282     /**
 283      * Returns face name for default font, or null if
 284      * no face names are used for CompositeFontDescriptors
 285      * for this platform.
 286      */
 287     public String getDefaultFontFaceName() {
 288 
 289         return null;
 290     }
 291 
 292     private static native boolean pRunningXinerama();
 293     private static native Point getXineramaCenterPoint();
 294 
 295     /**
 296      * Override for Xinerama case: call new Solaris API for getting the correct
 297      * centering point from the windowing system.
 298      */
 299     public Point getCenterPoint() {
 300         if (runningXinerama()) {
 301             Point p = getXineramaCenterPoint();
 302             if (p != null) {
 303                 return p;
 304             }
 305         }
 306         return super.getCenterPoint();
 307     }
 308 
 309     /**
 310      * Override for Xinerama case
 311      */
 312     public Rectangle getMaximumWindowBounds() {
 313         if (runningXinerama()) {
 314             return getXineramaWindowBounds();
 315         } else {
 316             return super.getMaximumWindowBounds();
 317         }
 318     }
 319 
 320     public boolean runningXinerama() {
 321         if (xinerState == null) {
 322             // pRunningXinerama() simply returns a global boolean variable,
 323             // so there is no need to synchronize here
 324             xinerState = Boolean.valueOf(pRunningXinerama());
 325             if (screenLog.isLoggable(PlatformLogger.Level.FINER)) {
 326                 screenLog.finer("Running Xinerama: " + xinerState);
 327             }
 328         }
 329         return xinerState.booleanValue();
 330     }
 331 
 332     /**
 333      * Return the bounds for a centered Window on a system running in Xinerama
 334      * mode.
 335      *
 336      * Calculations are based on the assumption of a perfectly rectangular
 337      * display area (display edges line up with one another, and displays
 338      * have consistent width and/or height).
 339      *
 340      * The bounds to return depend on the arrangement of displays and on where
 341      * Windows are to be centered.  There are two common situations:
 342      *
 343      * 1) The center point lies at the center of the combined area of all the
 344      *    displays.  In this case, the combined area of all displays is
 345      *    returned.
 346      *
 347      * 2) The center point lies at the center of a single display.  In this case
 348      *    the user most likely wants centered Windows to be constrained to that
 349      *    single display.  The boundaries of the one display are returned.
 350      *
 351      * It is possible for the center point to be at both the center of the
 352      * entire display space AND at the center of a single monitor (a square of
 353      * 9 monitors, for instance).  In this case, the entire display area is
 354      * returned.
 355      *
 356      * Because the center point is arbitrarily settable by the user, it could
 357      * fit neither of the cases above.  The fallback case is to simply return
 358      * the combined area for all screens.
 359      */
 360     protected Rectangle getXineramaWindowBounds() {
 361         Point center = getCenterPoint();
 362         Rectangle unionRect, tempRect;
 363         GraphicsDevice[] gds = getScreenDevices();
 364         Rectangle centerMonitorRect = null;
 365         int i;
 366 
 367         // if center point is at the center of all monitors
 368         // return union of all bounds
 369         //
 370         //  MM*MM     MMM       M
 371         //            M*M       *
 372         //            MMM       M
 373 
 374         // if center point is at center of a single monitor (but not of all
 375         // monitors)
 376         // return bounds of single monitor
 377         //
 378         // MMM         MM
 379         // MM*         *M
 380 
 381         // else, center is in some strange spot (such as on the border between
 382         // monitors), and we should just return the union of all monitors
 383         //
 384         // MM          MMM
 385         // MM          MMM
 386 
 387         unionRect = getUsableBounds(gds[0]);
 388 
 389         for (i = 0; i < gds.length; i++) {
 390             tempRect = getUsableBounds(gds[i]);
 391             if (centerMonitorRect == null &&
 392                 // add a pixel or two for fudge-factor
 393                 (tempRect.width / 2) + tempRect.x > center.x - 1 &&
 394                 (tempRect.height / 2) + tempRect.y > center.y - 1 &&
 395                 (tempRect.width / 2) + tempRect.x < center.x + 1 &&
 396                 (tempRect.height / 2) + tempRect.y < center.y + 1) {
 397                 centerMonitorRect = tempRect;
 398             }
 399             unionRect = unionRect.union(tempRect);
 400         }
 401 
 402         // first: check for center of all monitors (video wall)
 403         // add a pixel or two for fudge-factor
 404         if ((unionRect.width / 2) + unionRect.x > center.x - 1 &&
 405             (unionRect.height / 2) + unionRect.y > center.y - 1 &&
 406             (unionRect.width / 2) + unionRect.x < center.x + 1 &&
 407             (unionRect.height / 2) + unionRect.y < center.y + 1) {
 408 
 409             if (screenLog.isLoggable(PlatformLogger.Level.FINER)) {
 410                 screenLog.finer("Video Wall: center point is at center of all displays.");
 411             }
 412             return unionRect;
 413         }
 414 
 415         // next, check if at center of one monitor
 416         if (centerMonitorRect != null) {
 417             if (screenLog.isLoggable(PlatformLogger.Level.FINER)) {
 418                 screenLog.finer("Center point at center of a particular " +
 419                                 "monitor, but not of the entire virtual display.");
 420             }
 421             return centerMonitorRect;
 422         }
 423 
 424         // otherwise, the center is at some weird spot: return unionRect
 425         if (screenLog.isLoggable(PlatformLogger.Level.FINER)) {
 426             screenLog.finer("Center point is somewhere strange - return union of all bounds.");
 427         }
 428         return unionRect;
 429     }
 430 
 431     /**
 432      * From the DisplayChangedListener interface; devices do not need
 433      * to react to this event.
 434      */
 435     @Override
 436     public void paletteChanged() {
 437     }
 438 }