1 /*
2  * Copyright (c) 1997, 2021, 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.lang.ref.WeakReference;
31 import java.net.InetAddress;
32 import java.net.NetworkInterface;
33 import java.net.SocketException;
34 import java.net.UnknownHostException;
35 import java.util.ArrayList;
36 import java.util.Enumeration;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.ListIterator;
40 import java.util.Map;
41 
42 import sun.awt.X11.XToolkit;
43 import sun.java2d.SunGraphicsEnvironment;
44 import sun.java2d.SurfaceManagerFactory;
45 import sun.java2d.UnixSurfaceManagerFactory;
46 import sun.java2d.xr.XRSurfaceData;
47 
48 /**
49  * This is an implementation of a GraphicsEnvironment object for the
50  * default local GraphicsEnvironment used by the Java Runtime Environment
51  * for X11 environments.
52  *
53  * @see GraphicsDevice
54  * @see java.awt.GraphicsConfiguration
55  */
56 @SuppressWarnings("removal")
57 public final class X11GraphicsEnvironment extends SunGraphicsEnvironment {
58 
59     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; } })60         java.security.AccessController.doPrivileged(
61                           new java.security.PrivilegedAction<Object>() {
62             public Object run() {
63                 System.loadLibrary("awt");
64 
65                 /*
66                  * Note: The MToolkit object depends on the static initializer
67                  * of X11GraphicsEnvironment to initialize the connection to
68                  * the X11 server.
69                  */
70                 if (!isHeadless()) {
71                     // first check the OGL system property
72                     boolean glxRequested = false;
73                     String prop = System.getProperty("sun.java2d.opengl");
74                     if (prop != null) {
75                         if (prop.equals("true") || prop.equals("t")) {
76                             glxRequested = true;
77                         } else if (prop.equals("True") || prop.equals("T")) {
78                             glxRequested = true;
79                             glxVerbose = true;
80                         }
81                     }
82 
83                     // Now check for XRender system property
84                     boolean xRenderRequested = true;
85                     boolean xRenderIgnoreLinuxVersion = false;
86                     String xProp = System.getProperty("sun.java2d.xrender");
87                         if (xProp != null) {
88                         if (xProp.equals("false") || xProp.equals("f")) {
89                             xRenderRequested = false;
90                         } else if (xProp.equals("True") || xProp.equals("T")) {
91                             xRenderRequested = true;
92                             xRenderVerbose = true;
93                         }
94 
95                         if(xProp.equalsIgnoreCase("t") || xProp.equalsIgnoreCase("true")) {
96                             xRenderIgnoreLinuxVersion = true;
97                         }
98                     }
99 
100                     // initialize the X11 display connection
101                     initDisplay(glxRequested);
102 
103                     // only attempt to initialize GLX if it was requested
104                     if (glxRequested) {
105                         glxAvailable = initGLX();
106                         if (glxVerbose && !glxAvailable) {
107                             System.out.println(
108                                 "Could not enable OpenGL " +
109                                 "pipeline (GLX 1.3 not available)");
110                         }
111                     }
112 
113                     // only attempt to initialize Xrender if it was requested
114                     if (xRenderRequested) {
115                         xRenderAvailable = initXRender(xRenderVerbose, xRenderIgnoreLinuxVersion);
116                         if (xRenderVerbose && !xRenderAvailable) {
117                             System.out.println(
118                                          "Could not enable XRender pipeline");
119                         }
120                     }
121 
122                     if (xRenderAvailable) {
123                         XRSurfaceData.initXRSurfaceData();
124                     }
125                 }
126 
127                 return null;
128             }
129          });
130 
131         // Install the correct surface manager factory.
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory())132         SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
133 
134     }
135 
136 
137     private static boolean glxAvailable;
138     private static boolean glxVerbose;
139 
initGLX()140     private static native boolean initGLX();
141 
isGLXAvailable()142     public static boolean isGLXAvailable() {
143         return glxAvailable;
144     }
145 
isGLXVerbose()146     public static boolean isGLXVerbose() {
147         return glxVerbose;
148     }
149 
150     private static boolean xRenderVerbose;
151     private static boolean xRenderAvailable;
152 
initXRender(boolean verbose, boolean ignoreLinuxVersion)153     private static native boolean initXRender(boolean verbose, boolean ignoreLinuxVersion);
isXRenderAvailable()154     public static boolean isXRenderAvailable() {
155         return xRenderAvailable;
156     }
157 
isXRenderVerbose()158     public static boolean isXRenderVerbose() {
159         return xRenderVerbose;
160     }
161 
162     /**
163      * Checks if Shared Memory extension can be used.
164      * Returns:
165      *   -1 if server doesn't support MITShm
166      *    1 if server supports it and it can be used
167      *    0 otherwise
168      */
checkShmExt()169     private static native int checkShmExt();
170 
getDisplayString()171     private static  native String getDisplayString();
172     private Boolean isDisplayLocal;
173 
174     /** Available X11 screens. */
175     private final Map<Integer, X11GraphicsDevice> devices = new HashMap<>(5);
176 
177     /**
178      * The key in the {@link #devices} for the main screen.
179      */
180     private int mainScreen;
181 
182     // list of invalidated graphics devices (those which were removed)
183     private List<WeakReference<X11GraphicsDevice>> oldDevices = new ArrayList<>();
184 
185     /**
186      * This should only be called from the static initializer, so no need for
187      * the synchronized keyword.
188      */
initDisplay(boolean glxRequested)189     private static native void initDisplay(boolean glxRequested);
190 
getNumScreens()191     protected native int getNumScreens();
192 
getDefaultScreenNum()193     private native int getDefaultScreenNum();
194 
X11GraphicsEnvironment()195     public X11GraphicsEnvironment() {
196         if (isHeadless()) {
197             return;
198         }
199 
200         /* Populate the device table */
201         rebuildDevices();
202     }
203 
204     /**
205      * Initialize the native list of devices.
206      */
initNativeData()207     private static native void initNativeData();
208 
209     /**
210      * Updates the list of devices and notify listeners.
211      */
rebuildDevices()212     public void rebuildDevices() {
213         XToolkit.awtLock();
214         try {
215             initNativeData();
216             initDevices();
217         } finally {
218             XToolkit.awtUnlock();
219         }
220         displayChanged();
221     }
222 
223     /**
224      * (Re)create all X11GraphicsDevices, reuses a devices if it is possible.
225      */
initDevices()226     private synchronized void initDevices() {
227         Map<Integer, X11GraphicsDevice> old = new HashMap<>(devices);
228         devices.clear();
229 
230         int numScreens = getNumScreens();
231         if (numScreens == 0) {
232             throw new AWTError("no screen devices");
233         }
234         int index = getDefaultScreenNum();
235         mainScreen = 0 < index && index < screens.length ? index : 0;
236 
237         for (int id = 0; id < numScreens; ++id) {
238             devices.put(id, old.containsKey(id) ? old.remove(id) :
239                                                   new X11GraphicsDevice(id));
240         }
241         // if a device was not reused it should be invalidated
242         for (X11GraphicsDevice gd : old.values()) {
243             oldDevices.add(new WeakReference<>(gd));
244         }
245         // Need to notify old devices, in case the user hold the reference to it
246         for (ListIterator<WeakReference<X11GraphicsDevice>> it =
247              oldDevices.listIterator(); it.hasNext(); ) {
248             X11GraphicsDevice gd = it.next().get();
249             if (gd != null) {
250                 gd.invalidate(devices.get(mainScreen));
251                 gd.displayChanged();
252             } else {
253                 // no more references to this device, remove it
254                 it.remove();
255             }
256         }
257     }
258 
259     @Override
260     public synchronized GraphicsDevice getDefaultScreenDevice() {
261         return devices.get(mainScreen);
262     }
263 
264     @Override
265     public synchronized GraphicsDevice[] getScreenDevices() {
266         return devices.values().toArray(new X11GraphicsDevice[0]);
267     }
268 
269     public synchronized GraphicsDevice getScreenDevice(int screen) {
270         return devices.get(screen);
271     }
272 
273     @Override
274     protected GraphicsDevice makeScreenDevice(int screennum) {
275         throw new UnsupportedOperationException("This method is unused and" +
276                 "should not be called in this implementation");
277     }
278 
279     public boolean isDisplayLocal() {
280         if (isDisplayLocal == null) {
281             SunToolkit.awtLock();
282             try {
283                 if (isDisplayLocal == null) {
284                     isDisplayLocal = Boolean.valueOf(_isDisplayLocal());
285                 }
286             } finally {
287                 SunToolkit.awtUnlock();
288             }
289         }
290         return isDisplayLocal.booleanValue();
291     }
292 
293     private static boolean _isDisplayLocal() {
294         if (isHeadless()) {
295             return true;
296         }
297 
298         String isRemote = java.security.AccessController.doPrivileged(
299             new sun.security.action.GetPropertyAction("sun.java2d.remote"));
300         if (isRemote != null) {
301             return isRemote.equals("false");
302         }
303 
304         int shm = checkShmExt();
305         if (shm != -1) {
306             return (shm == 1);
307         }
308 
309         // If XServer doesn't support ShMem extension,
310         // try the other way
311 
312         String display = getDisplayString();
313         int ind = display.indexOf(':');
314         final String hostName = display.substring(0, ind);
315         if (ind <= 0) {
316             // ':0' case
317             return true;
318         }
319 
320         Boolean result = java.security.AccessController.doPrivileged(
321             new java.security.PrivilegedAction<Boolean>() {
322             public Boolean run() {
323                 InetAddress[] remAddr = null;
324                 Enumeration<InetAddress> locals = null;
325                 Enumeration<NetworkInterface> interfaces = null;
326                 try {
327                     interfaces = NetworkInterface.getNetworkInterfaces();
328                     remAddr = InetAddress.getAllByName(hostName);
329                     if (remAddr == null) {
330                         return Boolean.FALSE;
331                     }
332                 } catch (UnknownHostException e) {
333                     System.err.println("Unknown host: " + hostName);
334                     return Boolean.FALSE;
335                 } catch (SocketException e1) {
336                     System.err.println(e1.getMessage());
337                     return Boolean.FALSE;
338                 }
339 
340                 for (; interfaces.hasMoreElements();) {
341                     locals = interfaces.nextElement().getInetAddresses();
342                     for (; locals.hasMoreElements();) {
343                         final InetAddress localAddr = locals.nextElement();
344                         for (int i = 0; i < remAddr.length; i++) {
345                             if (localAddr.equals(remAddr[i])) {
346                                 return Boolean.TRUE;
347                             }
348                         }
349                     }
350                 }
351                 return Boolean.FALSE;
352             }});
353         return result.booleanValue();
354     }
355 
356 
357 
358     /**
359      * Returns face name for default font, or null if
360      * no face names are used for CompositeFontDescriptors
361      * for this platform.
362      */
363     public String getDefaultFontFaceName() {
364 
365         return null;
366     }
367 
368     private static native boolean pRunningXinerama();
369 
370     public boolean runningXinerama() {
371         return pRunningXinerama();
372     }
373 
374     /**
375      * From the DisplayChangedListener interface; devices do not need
376      * to react to this event.
377      */
378     @Override
379     public void paletteChanged() {
380     }
381 }
382