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