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