1/*
2 * Copyright (c) 1997, 2017, 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
27package java.awt;
28
29import java.awt.image.BufferedImage;
30import java.security.AccessController;
31import java.security.PrivilegedAction;
32import java.util.Locale;
33
34import sun.font.FontManager;
35import sun.font.FontManagerFactory;
36import sun.java2d.HeadlessGraphicsEnvironment;
37import sun.java2d.SunGraphicsEnvironment;
38import sun.security.action.GetPropertyAction;
39
40/**
41 *
42 * The {@code GraphicsEnvironment} class describes the collection
43 * of {@link GraphicsDevice} objects and {@link java.awt.Font} objects
44 * available to a Java(tm) application on a particular platform.
45 * The resources in this {@code GraphicsEnvironment} might be local
46 * or on a remote machine.  {@code GraphicsDevice} objects can be
47 * screens, printers or image buffers and are the destination of
48 * {@link Graphics2D} drawing methods.  Each {@code GraphicsDevice}
49 * has a number of {@link GraphicsConfiguration} objects associated with
50 * it.  These objects specify the different configurations in which the
51 * {@code GraphicsDevice} can be used.
52 * @see GraphicsDevice
53 * @see GraphicsConfiguration
54 */
55
56public abstract class GraphicsEnvironment {
57
58    /**
59     * The headless state of the Toolkit and GraphicsEnvironment
60     */
61    private static Boolean headless;
62
63    /**
64     * The headless state assumed by default
65     */
66    private static Boolean defaultHeadless;
67
68    /**
69     * This is an abstract class and cannot be instantiated directly.
70     * Instances must be obtained from a suitable factory or query method.
71     */
72    protected GraphicsEnvironment() {
73    }
74
75    /**
76     * Lazy initialization of local graphics environment using holder idiom.
77     */
78    private static final class LocalGE {
79
80        /**
81         * The instance of the local {@code GraphicsEnvironment}.
82         */
83        static final GraphicsEnvironment INSTANCE = createGE();
84
85        /**
86         * Creates and returns the GraphicsEnvironment, according to the
87         * system property 'java.awt.graphicsenv'.
88         *
89         * @return the graphics environment
90         */
91        private static GraphicsEnvironment createGE() {
92            GraphicsEnvironment ge;
93            String nm = AccessController.doPrivileged(new GetPropertyAction("java.awt.graphicsenv", null));
94            try {
95//              long t0 = System.currentTimeMillis();
96                Class<?> geCls;
97                try {
98                    // First we try if the bootstrap class loader finds the
99                    // requested class. This way we can avoid to run in a privileged
100                    // block.
101                    geCls = Class.forName(nm);
102                } catch (ClassNotFoundException ex) {
103                    // If the bootstrap class loader fails, we try again with the
104                    // application class loader.
105                    ClassLoader cl = ClassLoader.getSystemClassLoader();
106                    geCls = Class.forName(nm, true, cl);
107                }
108                ge = (GraphicsEnvironment)geCls.getConstructor().newInstance();
109//              long t1 = System.currentTimeMillis();
110//              System.out.println("GE creation took " + (t1-t0)+ "ms.");
111                if (isHeadless()) {
112                    ge = new HeadlessGraphicsEnvironment(ge);
113                }
114            } catch (ClassNotFoundException e) {
115                throw new Error("Could not find class: "+nm);
116            } catch (ReflectiveOperationException | IllegalArgumentException e) {
117                throw new Error("Could not instantiate Graphics Environment: "
118                        + nm);
119            }
120            return ge;
121        }
122    }
123
124    /**
125     * Returns the local {@code GraphicsEnvironment}.
126     * @return the local {@code GraphicsEnvironment}
127     */
128    public static GraphicsEnvironment getLocalGraphicsEnvironment() {
129        return LocalGE.INSTANCE;
130    }
131
132    /**
133     * Tests whether or not a display, keyboard, and mouse can be
134     * supported in this environment.  If this method returns true,
135     * a HeadlessException is thrown from areas of the Toolkit
136     * and GraphicsEnvironment that are dependent on a display,
137     * keyboard, or mouse.
138     * @return {@code true} if this environment cannot support
139     * a display, keyboard, and mouse; {@code false}
140     * otherwise
141     * @see java.awt.HeadlessException
142     * @since 1.4
143     */
144    public static boolean isHeadless() {
145        return getHeadlessProperty();
146    }
147
148    /**
149     * @return warning message if headless state is assumed by default;
150     * null otherwise
151     * @since 1.5
152     */
153    static String getHeadlessMessage() {
154        if (headless == null) {
155            getHeadlessProperty(); // initialize the values
156        }
157        return defaultHeadless != Boolean.TRUE ? null :
158            "\nNo X11 DISPLAY variable was set, " +
159            "but this program performed an operation which requires it.";
160    }
161
162    /**
163     * @return the value of the property "java.awt.headless"
164     * @since 1.4
165     */
166    private static boolean getHeadlessProperty() {
167        if (headless == null) {
168            AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
169                String nm = System.getProperty("java.awt.headless");
170
171                if (nm == null) {
172                    /* No need to ask for DISPLAY when run in a browser */
173                    if (System.getProperty("javaplugin.version") != null) {
174                        headless = defaultHeadless = Boolean.FALSE;
175                    } else {
176                        String osName = System.getProperty("os.name");
177                        if (osName.contains("OS X") && "sun.awt.HToolkit".equals(
178                                System.getProperty("awt.toolkit")))
179                        {
180                            headless = defaultHeadless = Boolean.TRUE;
181                        } else {
182                            final String display = System.getenv("DISPLAY");
183                            headless = defaultHeadless =
184                                ("Linux".equals(osName) ||
185                                 "SunOS".equals(osName) ||
186                                 "FreeBSD".equals(osName) ||
187                                 "NetBSD".equals(osName) ||
188                                 "OpenBSD".equals(osName) ||
189                                 "AIX".equals(osName)) &&
190                                 (display == null || display.trim().isEmpty());
191                        }
192                    }
193                } else {
194                    headless = Boolean.valueOf(nm);
195                }
196                return null;
197            });
198        }
199        return headless;
200    }
201
202    /**
203     * Check for headless state and throw HeadlessException if headless
204     * @since 1.4
205     */
206    static void checkHeadless() throws HeadlessException {
207        if (isHeadless()) {
208            throw new HeadlessException();
209        }
210    }
211
212    /**
213     * Returns whether or not a display, keyboard, and mouse can be
214     * supported in this graphics environment.  If this returns true,
215     * {@code HeadlessException} will be thrown from areas of the
216     * graphics environment that are dependent on a display, keyboard, or
217     * mouse.
218     * @return {@code true} if a display, keyboard, and mouse
219     * can be supported in this environment; {@code false}
220     * otherwise
221     * @see java.awt.HeadlessException
222     * @see #isHeadless
223     * @since 1.4
224     */
225    public boolean isHeadlessInstance() {
226        // By default (local graphics environment), simply check the
227        // headless property.
228        return getHeadlessProperty();
229    }
230
231    /**
232     * Returns an array of all of the screen {@code GraphicsDevice}
233     * objects.
234     * @return an array containing all the {@code GraphicsDevice}
235     * objects that represent screen devices
236     * @exception HeadlessException if isHeadless() returns true
237     * @see #isHeadless()
238     */
239    public abstract GraphicsDevice[] getScreenDevices()
240        throws HeadlessException;
241
242    /**
243     * Returns the default screen {@code GraphicsDevice}.
244     * @return the {@code GraphicsDevice} that represents the
245     * default screen device
246     * @exception HeadlessException if isHeadless() returns true
247     * @see #isHeadless()
248     */
249    public abstract GraphicsDevice getDefaultScreenDevice()
250        throws HeadlessException;
251
252    /**
253     * Returns a {@code Graphics2D} object for rendering into the
254     * specified {@link BufferedImage}.
255     * @param img the specified {@code BufferedImage}
256     * @return a {@code Graphics2D} to be used for rendering into
257     * the specified {@code BufferedImage}
258     * @throws NullPointerException if {@code img} is null
259     */
260    public abstract Graphics2D createGraphics(BufferedImage img);
261
262    /**
263     * Returns an array containing a one-point size instance of all fonts
264     * available in this {@code GraphicsEnvironment}.  Typical usage
265     * would be to allow a user to select a particular font.  Then, the
266     * application can size the font and set various font attributes by
267     * calling the {@code deriveFont} method on the chosen instance.
268     * <p>
269     * This method provides for the application the most precise control
270     * over which {@code Font} instance is used to render text.
271     * If a font in this {@code GraphicsEnvironment} has multiple
272     * programmable variations, only one
273     * instance of that {@code Font} is returned in the array, and
274     * other variations must be derived by the application.
275     * <p>
276     * If a font in this environment has multiple programmable variations,
277     * such as Multiple-Master fonts, only one instance of that font is
278     * returned in the {@code Font} array.  The other variations
279     * must be derived by the application.
280     *
281     * @return an array of {@code Font} objects
282     * @see #getAvailableFontFamilyNames
283     * @see java.awt.Font
284     * @see java.awt.Font#deriveFont
285     * @see java.awt.Font#getFontName
286     * @since 1.2
287     */
288    public abstract Font[] getAllFonts();
289
290    /**
291     * Returns an array containing the names of all font families in this
292     * {@code GraphicsEnvironment} localized for the default locale,
293     * as returned by {@code Locale.getDefault()}.
294     * <p>
295     * Typical usage would be for presentation to a user for selection of
296     * a particular family name. An application can then specify this name
297     * when creating a font, in conjunction with a style, such as bold or
298     * italic, giving the font system flexibility in choosing its own best
299     * match among multiple fonts in the same font family.
300     *
301     * @return an array of {@code String} containing font family names
302     * localized for the default locale, or a suitable alternative
303     * name if no name exists for this locale.
304     * @see #getAllFonts
305     * @see java.awt.Font
306     * @see java.awt.Font#getFamily
307     * @since 1.2
308     */
309    public abstract String[] getAvailableFontFamilyNames();
310
311    /**
312     * Returns an array containing the names of all font families in this
313     * {@code GraphicsEnvironment} localized for the specified locale.
314     * <p>
315     * Typical usage would be for presentation to a user for selection of
316     * a particular family name. An application can then specify this name
317     * when creating a font, in conjunction with a style, such as bold or
318     * italic, giving the font system flexibility in choosing its own best
319     * match among multiple fonts in the same font family.
320     *
321     * @param l a {@link Locale} object that represents a
322     * particular geographical, political, or cultural region.
323     * Specifying {@code null} is equivalent to
324     * specifying {@code Locale.getDefault()}.
325     * @return an array of {@code String} containing font family names
326     * localized for the specified {@code Locale}, or a
327     * suitable alternative name if no name exists for the specified locale.
328     * @see #getAllFonts
329     * @see java.awt.Font
330     * @see java.awt.Font#getFamily
331     * @since 1.2
332     */
333    public abstract String[] getAvailableFontFamilyNames(Locale l);
334
335    /**
336     * Registers a <i>created</i> {@code Font} in this
337     * {@code GraphicsEnvironment}.
338     * A created font is one that was returned from calling
339     * {@link Font#createFont}, or derived from a created font by
340     * calling {@link Font#deriveFont}.
341     * After calling this method for such a font, it is available to
342     * be used in constructing new {@code Font}s by name or family name,
343     * and is enumerated by {@link #getAvailableFontFamilyNames} and
344     * {@link #getAllFonts} within the execution context of this
345     * application or applet. This means applets cannot register fonts in
346     * a way that they are visible to other applets.
347     * <p>
348     * Reasons that this method might not register the font and therefore
349     * return {@code false} are:
350     * <ul>
351     * <li>The font is not a <i>created</i> {@code Font}.
352     * <li>The font conflicts with a non-created {@code Font} already
353     * in this {@code GraphicsEnvironment}. For example if the name
354     * is that of a system font, or a logical font as described in the
355     * documentation of the {@link Font} class. It is implementation dependent
356     * whether a font may also conflict if it has the same family name
357     * as a system font.
358     * <p>Notice that an application can supersede the registration
359     * of an earlier created font with a new one.
360     * </ul>
361     *
362     * @param  font the font to be registered
363     * @return true if the {@code font} is successfully
364     * registered in this {@code GraphicsEnvironment}.
365     * @throws NullPointerException if {@code font} is null
366     * @since 1.6
367     */
368    public boolean registerFont(Font font) {
369        if (font == null) {
370            throw new NullPointerException("font cannot be null.");
371        }
372        FontManager fm = FontManagerFactory.getInstance();
373        return fm.registerFont(font);
374    }
375
376    /**
377     * Indicates a preference for locale-specific fonts in the mapping of
378     * logical fonts to physical fonts. Calling this method indicates that font
379     * rendering should primarily use fonts specific to the primary writing
380     * system (the one indicated by the default encoding and the initial
381     * default locale). For example, if the primary writing system is
382     * Japanese, then characters should be rendered using a Japanese font
383     * if possible, and other fonts should only be used for characters for
384     * which the Japanese font doesn't have glyphs.
385     * <p>
386     * The actual change in font rendering behavior resulting from a call
387     * to this method is implementation dependent; it may have no effect at
388     * all, or the requested behavior may already match the default behavior.
389     * The behavior may differ between font rendering in lightweight
390     * and peered components.  Since calling this method requests a
391     * different font, clients should expect different metrics, and may need
392     * to recalculate window sizes and layout. Therefore this method should
393     * be called before user interface initialisation.
394     * @since 1.5
395     */
396    public void preferLocaleFonts() {
397        FontManager fm = FontManagerFactory.getInstance();
398        fm.preferLocaleFonts();
399    }
400
401    /**
402     * Indicates a preference for proportional over non-proportional (e.g.
403     * dual-spaced CJK fonts) fonts in the mapping of logical fonts to
404     * physical fonts. If the default mapping contains fonts for which
405     * proportional and non-proportional variants exist, then calling
406     * this method indicates the mapping should use a proportional variant.
407     * <p>
408     * The actual change in font rendering behavior resulting from a call to
409     * this method is implementation dependent; it may have no effect at all.
410     * The behavior may differ between font rendering in lightweight and
411     * peered components. Since calling this method requests a
412     * different font, clients should expect different metrics, and may need
413     * to recalculate window sizes and layout. Therefore this method should
414     * be called before user interface initialisation.
415     * @since 1.5
416     */
417    public void preferProportionalFonts() {
418        FontManager fm = FontManagerFactory.getInstance();
419        fm.preferProportionalFonts();
420    }
421
422    /**
423     * Returns the Point where Windows should be centered.
424     * It is recommended that centered Windows be checked to ensure they fit
425     * within the available display area using getMaximumWindowBounds().
426     * @return the point where Windows should be centered
427     *
428     * @exception HeadlessException if isHeadless() returns true
429     * @see #getMaximumWindowBounds
430     * @since 1.4
431     */
432    public Point getCenterPoint() throws HeadlessException {
433    // Default implementation: return the center of the usable bounds of the
434    // default screen device.
435        Rectangle usableBounds =
436         SunGraphicsEnvironment.getUsableBounds(getDefaultScreenDevice());
437        return new Point((usableBounds.width / 2) + usableBounds.x,
438                         (usableBounds.height / 2) + usableBounds.y);
439    }
440
441    /**
442     * Returns the maximum bounds for centered Windows.
443     * These bounds account for objects in the native windowing system such as
444     * task bars and menu bars.  The returned bounds will reside on a single
445     * display with one exception: on multi-screen systems where Windows should
446     * be centered across all displays, this method returns the bounds of the
447     * entire display area.
448     * <p>
449     * To get the usable bounds of a single display, use
450     * {@code GraphicsConfiguration.getBounds()} and
451     * {@code Toolkit.getScreenInsets()}.
452     * @return  the maximum bounds for centered Windows
453     *
454     * @exception HeadlessException if isHeadless() returns true
455     * @see #getCenterPoint
456     * @see GraphicsConfiguration#getBounds
457     * @see Toolkit#getScreenInsets
458     * @since 1.4
459     */
460    public Rectangle getMaximumWindowBounds() throws HeadlessException {
461    // Default implementation: return the usable bounds of the default screen
462    // device.  This is correct for Microsoft Windows and non-Xinerama X11.
463        return SunGraphicsEnvironment.getUsableBounds(getDefaultScreenDevice());
464    }
465}
466