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