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.java2d;
  27 
  28 import java.awt.AWTError;
  29 import java.awt.Color;
  30 import java.awt.Font;
  31 import java.awt.Graphics2D;
  32 import java.awt.GraphicsConfiguration;
  33 import java.awt.GraphicsDevice;
  34 import java.awt.GraphicsEnvironment;
  35 import java.awt.Insets;
  36 import java.awt.Rectangle;
  37 import java.awt.Toolkit;
  38 import java.awt.font.TextAttribute;
  39 import java.awt.image.BufferedImage;
  40 import java.awt.peer.ComponentPeer;
  41 import java.io.BufferedReader;
  42 import java.io.File;
  43 import java.io.FileInputStream;
  44 import java.io.FilenameFilter;
  45 import java.io.InputStreamReader;
  46 import java.io.IOException;
  47 import java.text.AttributedCharacterIterator;
  48 import java.util.ArrayList;
  49 import java.util.HashSet;
  50 import java.util.Iterator;
  51 import java.util.Locale;
  52 import java.util.Map;
  53 import java.util.NoSuchElementException;
  54 import java.util.Set;
  55 import java.util.StringTokenizer;
  56 import java.util.TreeMap;
  57 import java.util.Vector;
  58 import java.util.concurrent.ConcurrentHashMap;
  59 import sun.awt.AppContext;
  60 import sun.awt.DisplayChangedListener;
  61 import sun.awt.FontConfiguration;
  62 import sun.awt.SunDisplayChanger;
  63 import sun.font.CompositeFontDescriptor;
  64 import sun.font.Font2D;
  65 import sun.font.FontManager;
  66 import sun.font.FontManagerFactory;
  67 import sun.font.FontManagerForSGE;
  68 import sun.font.NativeFont;
  69 
  70 /**
  71  * This is an implementation of a GraphicsEnvironment object for the
  72  * default local GraphicsEnvironment.
  73  *
  74  * @see GraphicsDevice
  75  * @see GraphicsConfiguration
  76  */
  77 public abstract class SunGraphicsEnvironment extends GraphicsEnvironment
  78     implements DisplayChangedListener {
  79 
  80     public static boolean isOpenSolaris;
  81     private static Font defaultFont;
  82 
  83     public SunGraphicsEnvironment() {
  84         java.security.AccessController.doPrivileged(
  85                                     new java.security.PrivilegedAction<Object>() {
  86             public Object run() {
  87                 String osName = System.getProperty("os.name");
  88                 if ("SunOS".equals(osName)) {
  89                     String version = System.getProperty("os.version", "0.0");
  90                     try {
  91                         float ver = Float.parseFloat(version);
  92                         if (ver > 5.10f) {
  93                             File f = new File("/etc/release");
  94                             FileInputStream fis = new FileInputStream(f);
  95                             InputStreamReader isr
  96                                 = new InputStreamReader(fis, "ISO-8859-1");
  97                             BufferedReader br = new BufferedReader(isr);
  98                             String line = br.readLine();
  99                             if (line.indexOf("OpenSolaris") >= 0) {
 100                                 isOpenSolaris = true;
 101                             } else {
 102                                 /* We are using isOpenSolaris as meaning
 103                                  * we know the Solaris commercial fonts aren't
 104                                  * present. "Solaris Next" (03/10) did not
 105                                  * include these even though its was not
 106                                  * OpenSolaris. Need to revisit how this is
 107                                  * handled but for now as in 6ux, we'll use
 108                                  * the test for a standard font resource as
 109                                  * being an indicator as to whether we need
 110                                  * to treat this as OpenSolaris from a font
 111                                  * config perspective.
 112                                  */
 113                                 String courierNew =
 114                                     "/usr/openwin/lib/X11/fonts/TrueType/CourierNew.ttf";
 115                                 File courierFile = new File(courierNew);
 116                                 isOpenSolaris = !courierFile.exists();
 117                             }
 118                             fis.close();
 119                         }
 120                     } catch (Exception e) {
 121                     }
 122                 }
 123                 /* Establish the default font to be used by SG2D etc */
 124                 defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12);
 125 
 126                 return null;
 127             }
 128         });
 129     }
 130 
 131     protected GraphicsDevice[] screens;
 132 
 133     /**
 134      * Returns an array of all of the screen devices.
 135      */
 136     public synchronized GraphicsDevice[] getScreenDevices() {
 137         GraphicsDevice[] ret = screens;
 138         if (ret == null) {
 139             int num = getNumScreens();
 140             ret = new GraphicsDevice[num];
 141             for (int i = 0; i < num; i++) {
 142                 ret[i] = makeScreenDevice(i);
 143             }
 144             screens = ret;
 145         }
 146         return ret;
 147     }
 148 
 149     /**
 150      * Returns the number of screen devices of this graphics environment.
 151      *
 152      * @return the number of screen devices of this graphics environment
 153      */
 154     protected abstract int getNumScreens();
 155 
 156     /**
 157      * Create and return the screen device with the specified number. The
 158      * device with number <code>0</code> will be the default device (returned
 159      * by {@link #getDefaultScreenDevice()}.
 160      *
 161      * @param screennum the number of the screen to create
 162      *
 163      * @return the created screen device
 164      */
 165     protected abstract GraphicsDevice makeScreenDevice(int screennum);
 166 
 167     /**
 168      * Returns the default screen graphics device.
 169      */
 170     public GraphicsDevice getDefaultScreenDevice() {
 171         GraphicsDevice[] screens = getScreenDevices();
 172         if (screens.length == 0) {
 173             throw new AWTError("no screen devices");
 174         }
 175         return screens[0];
 176     }
 177 
 178     /**
 179      * Returns a Graphics2D object for rendering into the
 180      * given BufferedImage.
 181      * @throws NullPointerException if BufferedImage argument is null
 182      */
 183     public Graphics2D createGraphics(BufferedImage img) {
 184         if (img == null) {
 185             throw new NullPointerException("BufferedImage cannot be null");
 186         }
 187         SurfaceData sd = SurfaceData.getPrimarySurfaceData(img);
 188         return new SunGraphics2D(sd, Color.white, Color.black, defaultFont);
 189     }
 190 
 191     public static FontManagerForSGE getFontManagerForSGE() {
 192         FontManager fm = FontManagerFactory.getInstance();
 193         return (FontManagerForSGE) fm;
 194     }
 195 
 196     /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
 197      * to use Mincho instead of Gothic for dialoginput in JA locales
 198      * on windows. Not needed on other platforms.
 199      *
 200      * DO NOT MOVE OR RENAME OR OTHERWISE ALTER THIS METHOD.
 201      * ITS USED BY SOME NON-JRE INTERNAL CODE.
 202      */
 203     public static void useAlternateFontforJALocales() {
 204         getFontManagerForSGE().useAlternateFontforJALocales();
 205     }
 206 
 207      /**
 208      * Returns all fonts available in this environment.
 209      */
 210     public Font[] getAllFonts() {
 211         FontManagerForSGE fm = getFontManagerForSGE();
 212         Font[] installedFonts = fm.getAllInstalledFonts();
 213         Font[] created = fm.getCreatedFonts();
 214         if (created == null || created.length == 0) {
 215             return installedFonts;
 216         } else {
 217             int newlen = installedFonts.length + created.length;
 218             Font [] fonts = java.util.Arrays.copyOf(installedFonts, newlen);
 219             System.arraycopy(created, 0, fonts,
 220                              installedFonts.length, created.length);
 221             return fonts;
 222         }
 223     }
 224 
 225     public String[] getAvailableFontFamilyNames(Locale requestedLocale) {
 226         FontManagerForSGE fm = getFontManagerForSGE();
 227         String[] installed = fm.getInstalledFontFamilyNames(requestedLocale);
 228         /* Use a new TreeMap as used in getInstalledFontFamilyNames
 229          * and insert all the keys in lower case, so that the sort order
 230          * is the same as the installed families. This preserves historical
 231          * behaviour and inserts new families in the right place.
 232          * It would have been marginally more efficient to directly obtain
 233          * the tree map and just insert new entries, but not so much as
 234          * to justify the extra internal interface.
 235          */
 236         TreeMap<String, String> map = fm.getCreatedFontFamilyNames();
 237         if (map == null || map.size() == 0) {
 238             return installed;
 239         } else {
 240             for (int i=0; i<installed.length; i++) {
 241                 map.put(installed[i].toLowerCase(requestedLocale),
 242                         installed[i]);
 243             }
 244             String[] retval =  new String[map.size()];
 245             Object [] keyNames = map.keySet().toArray();
 246             for (int i=0; i < keyNames.length; i++) {
 247                 retval[i] = map.get(keyNames[i]);
 248             }
 249             return retval;
 250         }
 251     }
 252 
 253     public String[] getAvailableFontFamilyNames() {
 254         return getAvailableFontFamilyNames(Locale.getDefault());
 255     }
 256 
 257     /**
 258      * Return the bounds of a GraphicsDevice, less its screen insets.
 259      * See also java.awt.GraphicsEnvironment.getUsableBounds();
 260      */
 261     public static Rectangle getUsableBounds(GraphicsDevice gd) {
 262         GraphicsConfiguration gc = gd.getDefaultConfiguration();
 263         Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
 264         Rectangle usableBounds = gc.getBounds();
 265 
 266         usableBounds.x += insets.left;
 267         usableBounds.y += insets.top;
 268         usableBounds.width -= (insets.left + insets.right);
 269         usableBounds.height -= (insets.top + insets.bottom);
 270 
 271         return usableBounds;
 272     }
 273 
 274     /**
 275      * From the DisplayChangedListener interface; called
 276      * when the display mode has been changed.
 277      */
 278     public void displayChanged() {
 279         // notify screens in device array to do display update stuff
 280         for (GraphicsDevice gd : getScreenDevices()) {
 281             if (gd instanceof DisplayChangedListener) {
 282                 ((DisplayChangedListener) gd).displayChanged();
 283             }
 284         }
 285 
 286         // notify SunDisplayChanger list (e.g. VolatileSurfaceManagers and
 287         // SurfaceDataProxies) about the display change event
 288         displayChanger.notifyListeners();
 289     }
 290 
 291     /**
 292      * Part of the DisplayChangedListener interface:
 293      * propagate this event to listeners
 294      */
 295     public void paletteChanged() {
 296         displayChanger.notifyPaletteChanged();
 297     }
 298 
 299     /**
 300      * Returns true when the display is local, false for remote displays.
 301      *
 302      * @return true when the display is local, false for remote displays
 303      */
 304     public abstract boolean isDisplayLocal();
 305 
 306     /*
 307      * ----DISPLAY CHANGE SUPPORT----
 308      */
 309 
 310     protected SunDisplayChanger displayChanger = new SunDisplayChanger();
 311 
 312     /**
 313      * Add a DisplayChangeListener to be notified when the display settings
 314      * are changed.
 315      */
 316     public void addDisplayChangedListener(DisplayChangedListener client) {
 317         displayChanger.add(client);
 318     }
 319 
 320     /**
 321      * Remove a DisplayChangeListener from Win32GraphicsEnvironment
 322      */
 323     public void removeDisplayChangedListener(DisplayChangedListener client) {
 324         displayChanger.remove(client);
 325     }
 326 
 327     /*
 328      * ----END DISPLAY CHANGE SUPPORT----
 329      */
 330 
 331     /**
 332      * Returns true if FlipBufferStrategy with COPIED buffer contents
 333      * is preferred for this peer's GraphicsConfiguration over
 334      * BlitBufferStrategy, false otherwise.
 335      *
 336      * The reason FlipBS could be preferred is that in some configurations
 337      * an accelerated copy to the screen is supported (like Direct3D 9)
 338      *
 339      * @return true if flip strategy should be used, false otherwise
 340      */
 341     public boolean isFlipStrategyPreferred(ComponentPeer peer) {
 342         return false;
 343     }
 344 }