1 /*
2  * Copyright (c) 1997, 2018, 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 {
java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Object>() { public Object run() { System.loadLibrary(R); if (!isHeadless()) { boolean glxRequested = false; String prop = System.getProperty(R); if (prop != null) { if (prop.equals(R) || prop.equals(R)) { glxRequested = true; } else if (prop.equals(R) || prop.equals(R)) { glxRequested = true; glxVerbose = true; } } boolean xRenderRequested = true; boolean xRenderIgnoreLinuxVersion = false; String xProp = System.getProperty(R); if (xProp != null) { if (xProp.equals(R) || xProp.equals(R)) { xRenderRequested = false; } else if (xProp.equals(R) || xProp.equals(R)) { xRenderRequested = true; xRenderVerbose = true; } if(xProp.equalsIgnoreCase(R) || xProp.equalsIgnoreCase(R)) { xRenderIgnoreLinuxVersion = true; } } initDisplay(glxRequested); if (glxRequested) { glxAvailable = initGLX(); if (glxVerbose && !glxAvailable) { System.out.println( R + R); } } if (xRenderRequested) { xRenderAvailable = initXRender(xRenderVerbose, xRenderIgnoreLinuxVersion); if (xRenderVerbose && !xRenderAvailable) { System.out.println( R); } } if (xRenderAvailable) { XRSurfaceData.initXRSurfaceData(); } } return null; } })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.
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory())133         SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
134 
135     }
136 
137 
138     private static boolean glxAvailable;
139     private static boolean glxVerbose;
140 
initGLX()141     private static native boolean initGLX();
142 
isGLXAvailable()143     public static boolean isGLXAvailable() {
144         return glxAvailable;
145     }
146 
isGLXVerbose()147     public static boolean isGLXVerbose() {
148         return glxVerbose;
149     }
150 
151     private static boolean xRenderVerbose;
152     private static boolean xRenderAvailable;
153 
initXRender(boolean verbose, boolean ignoreLinuxVersion)154     private static native boolean initXRender(boolean verbose, boolean ignoreLinuxVersion);
isXRenderAvailable()155     public static boolean isXRenderAvailable() {
156         return xRenderAvailable;
157     }
158 
isXRenderVerbose()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      */
checkShmExt()170     private static native int checkShmExt();
171 
getDisplayString()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      */
initDisplay(boolean glxRequested)179     private static native void initDisplay(boolean glxRequested);
180 
X11GraphicsEnvironment()181     public X11GraphicsEnvironment() {
182     }
183 
getNumScreens()184     protected native int getNumScreens();
185 
makeScreenDevice(int screennum)186     protected GraphicsDevice makeScreenDevice(int screennum) {
187         return new X11GraphicsDevice(screennum);
188     }
189 
getDefaultScreenNum()190     private native int getDefaultScreenNum();
191     /**
192      * Returns the default screen graphics device.
193      */
getDefaultScreenDevice()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 
isDisplayLocal()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 
_isDisplayLocal()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      */
getDefaultFontFaceName()287     public String getDefaultFontFaceName() {
288 
289         return null;
290     }
291 
pRunningXinerama()292     private static native boolean pRunningXinerama();
getXineramaCenterPoint()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      */
getCenterPoint()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      */
getMaximumWindowBounds()312     public Rectangle getMaximumWindowBounds() {
313         if (runningXinerama()) {
314             return getXineramaWindowBounds();
315         } else {
316             return super.getMaximumWindowBounds();
317         }
318     }
319 
runningXinerama()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      */
getXineramaWindowBounds()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
paletteChanged()436     public void paletteChanged() {
437     }
438 }
439