0001: /*
0002: * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.awt;
0027:
0028: import java.awt.GraphicsDevice;
0029: import java.awt.Point;
0030: import java.awt.Rectangle;
0031: import java.io.BufferedReader;
0032: import java.io.File;
0033: import java.io.FileReader;
0034: import java.io.FileNotFoundException;
0035: import java.io.InputStream;
0036: import java.io.IOException;
0037: import java.io.StreamTokenizer;
0038: import java.net.InetAddress;
0039: import java.net.NetworkInterface;
0040: import java.net.SocketException;
0041: import java.net.UnknownHostException;
0042: import java.util.Enumeration;
0043: import java.util.HashMap;
0044: import java.util.HashSet;
0045: import java.util.Iterator;
0046: import java.util.Locale;
0047: import java.util.List;
0048: import java.util.Map;
0049: import java.util.NoSuchElementException;
0050: import java.util.StringTokenizer;
0051: import java.util.TreeMap;
0052: import java.util.Vector;
0053: import sun.awt.motif.MFontConfiguration;
0054: import sun.font.Font2D;
0055: import sun.font.FontManager;
0056: import sun.font.NativeFont;
0057: import sun.java2d.SunGraphicsEnvironment;
0058:
0059: /**
0060: * This is an implementation of a GraphicsEnvironment object for the
0061: * default local GraphicsEnvironment used by the Java Runtime Environment
0062: * for X11 environments.
0063: *
0064: * @see GraphicsDevice
0065: * @see GraphicsConfiguration
0066: * @version 1.77 05/05/07
0067: */
0068: public class X11GraphicsEnvironment extends SunGraphicsEnvironment {
0069: private static final DebugHelper dbg = DebugHelper
0070: .create(X11GraphicsEnvironment.class);
0071: private static Boolean xinerState;
0072:
0073: /*
0074: * This is the set of font directories needed to be on the X font path
0075: * to enable AWT heavyweights to find all of the font configuration fonts.
0076: * It is populated by :
0077: * - awtfontpath entries in the fontconfig.properties
0078: * - parent directories of "core" fonts used in the fontconfig.properties
0079: * - looking up font dirs in the xFontDirsMap where the key is a fontID
0080: * (cut down version of the XLFD read from the font configuration file).
0081: * This set is nulled out after use to free heap space.
0082: */
0083: private static HashSet<String> fontConfigDirs = null;
0084:
0085: /*
0086: * fontNameMap is a map from a fontID (which is a substring of an XLFD like
0087: * "-monotype-arial-bold-r-normal-iso8859-7")
0088: * to font file path like
0089: * /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf
0090: * It's used in a couple of methods like
0091: * getFileNameFomPlatformName(..) to help locate the font file.
0092: * We use this substring of a full XLFD because the font configuration files
0093: * define the XLFDs in a way that's easier to make into a request.
0094: * E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font
0095: * configuration files. We need to remove that part for comparisons.
0096: */
0097: private static Map fontNameMap = new HashMap();
0098:
0099: /* xFontDirsMap is also a map from a font ID to a font filepath.
0100: * The difference from fontNameMap is just that it does not have
0101: * resolved symbolic links. Normally this is not interesting except
0102: * that we need to know the directory in which a font was found to
0103: * add it to the X font server path, since although the files may
0104: * be linked, the fonts.dir is different and specific to the encoding
0105: * handled by that directory. This map is nulled out after use to free
0106: * heap space. If the optimal path is taken, such that all fonts in
0107: * font configuration files are referenced by filename, then the font
0108: * dir can be directly derived as its parent directory.
0109: * If a font is used by two XLFDs, each corresponding to a different
0110: * X11 font directory, then precautions must be taken to include both
0111: * directories.
0112: */
0113: private static Map xFontDirsMap;
0114:
0115: /*
0116: * xlfdMap is a map from a platform path like
0117: * /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like
0118: * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
0119: * Because there may be multiple native names, because the font is used
0120: * to support multiple X encodings for example, the value of an entry in
0121: * this map is always a vector where we store all the native names.
0122: * For fonts which we don't understand the key isn't a pathname, its
0123: * the full XLFD string like :-
0124: * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
0125: */
0126: private static Map xlfdMap = new HashMap();
0127:
0128: /*
0129: * Used to eliminate redundant work. When a font directory is
0130: * registered it added to this list. Subsequent registrations for the
0131: * same directory can then be skipped by checking this Map.
0132: * Access to this map is not synchronised here since creation
0133: * of the singleton GE instance is already synchronised and that is
0134: * the only code path that accesses this map.
0135: */
0136: private static HashMap registeredDirs = new HashMap();
0137:
0138: /* Array of directories to be added to the X11 font path.
0139: * Used by static method called from Toolkits which use X11 fonts.
0140: * Specifically this means MToolkit
0141: */
0142: private static String[] fontdirs = null;
0143:
0144: static {
0145: java.security.AccessController
0146: .doPrivileged(new java.security.PrivilegedAction() {
0147: public Object run() {
0148: System.loadLibrary("awt");
0149:
0150: /*
0151: * Note: The MToolkit object depends on the static initializer
0152: * of X11GraphicsEnvironment to initialize the connection to
0153: * the X11 server.
0154: */
0155: if (!isHeadless()) {
0156: // first check the OGL system property
0157: boolean glxRequested = false;
0158: String prop = System
0159: .getProperty("sun.java2d.opengl");
0160: if (prop != null) {
0161: if (prop.equals("true")
0162: || prop.equals("t")) {
0163: glxRequested = true;
0164: } else if (prop.equals("True")
0165: || prop.equals("T")) {
0166: glxRequested = true;
0167: glxVerbose = true;
0168: }
0169: }
0170:
0171: // initialize the X11 display connection
0172: initDisplay(glxRequested);
0173:
0174: // only attempt to initialize GLX if it was requested
0175: if (glxRequested) {
0176: glxAvailable = initGLX();
0177: if (glxVerbose && !glxAvailable) {
0178: System.out
0179: .println("Could not enable OpenGL "
0180: + "pipeline (GLX 1.3 not available)");
0181: }
0182: }
0183: }
0184:
0185: return null;
0186: }
0187: });
0188: }
0189:
0190: private static boolean glxAvailable;
0191: private static boolean glxVerbose;
0192:
0193: private static native boolean initGLX();
0194:
0195: public static boolean isGLXAvailable() {
0196: return glxAvailable;
0197: }
0198:
0199: public static boolean isGLXVerbose() {
0200: return glxVerbose;
0201: }
0202:
0203: /**
0204: * Checks if Shared Memory extension can be used.
0205: * Returns:
0206: * -1 if server doesn't support MITShm
0207: * 1 if server supports it and it can be used
0208: * 0 otherwise
0209: */
0210: private static native int checkShmExt();
0211:
0212: private static native String getDisplayString();
0213:
0214: private static Boolean isDisplayLocal;
0215:
0216: /**
0217: * This should only be called from the static initializer, so no need for
0218: * the synchronized keyword.
0219: */
0220: private static native void initDisplay(boolean glxRequested);
0221:
0222: public X11GraphicsEnvironment() {
0223: }
0224:
0225: protected native int getNumScreens();
0226:
0227: protected GraphicsDevice makeScreenDevice(int screennum) {
0228: return new X11GraphicsDevice(screennum);
0229: }
0230:
0231: protected native int getDefaultScreenNum();
0232:
0233: /**
0234: * Returns the default screen graphics device.
0235: */
0236: public GraphicsDevice getDefaultScreenDevice() {
0237: return getScreenDevices()[getDefaultScreenNum()];
0238: }
0239:
0240: public static boolean isDisplayLocal() {
0241: if (isDisplayLocal == null) {
0242: SunToolkit.awtLock();
0243: try {
0244: if (isDisplayLocal == null) {
0245: isDisplayLocal = Boolean.valueOf(_isDisplayLocal());
0246: }
0247: } finally {
0248: SunToolkit.awtUnlock();
0249: }
0250: }
0251: return isDisplayLocal.booleanValue();
0252: }
0253:
0254: private static boolean _isDisplayLocal() {
0255: if (isHeadless()) {
0256: return true;
0257: }
0258:
0259: String isRemote = (String) java.security.AccessController
0260: .doPrivileged(new sun.security.action.GetPropertyAction(
0261: "sun.java2d.remote"));
0262: if (isRemote != null) {
0263: return isRemote.equals("false");
0264: }
0265:
0266: int shm = checkShmExt();
0267: if (shm != -1) {
0268: return (shm == 1);
0269: }
0270:
0271: // If XServer doesn't support ShMem extension,
0272: // try the other way
0273:
0274: String display = getDisplayString();
0275: int ind = display.indexOf(':');
0276: final String hostName = display.substring(0, ind);
0277: if (ind <= 0) {
0278: // ':0' case
0279: return true;
0280: }
0281:
0282: Boolean result = (Boolean) java.security.AccessController
0283: .doPrivileged(new java.security.PrivilegedAction() {
0284: public Object run() {
0285: InetAddress remAddr[] = null;
0286: Enumeration locals = null;
0287: Enumeration interfaces = null;
0288: try {
0289: interfaces = NetworkInterface
0290: .getNetworkInterfaces();
0291: remAddr = InetAddress
0292: .getAllByName(hostName);
0293: if (remAddr == null) {
0294: return Boolean.FALSE;
0295: }
0296: } catch (UnknownHostException e) {
0297: System.err.println("Unknown host: "
0298: + hostName);
0299: return Boolean.FALSE;
0300: } catch (SocketException e1) {
0301: System.err.println(e1.getMessage());
0302: return Boolean.FALSE;
0303: }
0304:
0305: for (; interfaces.hasMoreElements();) {
0306: locals = ((NetworkInterface) interfaces
0307: .nextElement()).getInetAddresses();
0308: for (; locals.hasMoreElements();) {
0309: for (int i = 0; i < remAddr.length; i++) {
0310: if (locals.nextElement().equals(
0311: remAddr[i])) {
0312: return Boolean.TRUE;
0313: }
0314: }
0315: }
0316: }
0317: return Boolean.FALSE;
0318: }
0319: });
0320: return result.booleanValue();
0321: }
0322:
0323: /* These maps are used on Linux where we reference the Lucida oblique
0324: * fonts in fontconfig files even though they aren't in the standard
0325: * font directory. This explicitly remaps the XLFDs for these to the
0326: * correct base font. This is needed to prevent composite fonts from
0327: * defaulting to the Lucida Sans which is a bad substitute for the
0328: * monospaced Lucida Sans Typewriter. Also these maps prevent the
0329: * JRE from doing wasted work at start up.
0330: */
0331: HashMap<String, String> oblmap = null;
0332:
0333: private String getObliqueLucidaFontID(String fontID) {
0334: if (fontID.startsWith("-lucidasans-medium-i-normal")
0335: || fontID.startsWith("-lucidasans-bold-i-normal")
0336: || fontID
0337: .startsWith("-lucidatypewriter-medium-i-normal")
0338: || fontID.startsWith("-lucidatypewriter-bold-i-normal")) {
0339: return fontID.substring(0, fontID.indexOf("-i-"));
0340: } else {
0341: return null;
0342: }
0343: }
0344:
0345: private void initObliqueLucidaFontMap() {
0346: oblmap = new HashMap<String, String>();
0347: oblmap.put("-lucidasans-medium", jreLibDirName
0348: + "/fonts/LucidaSansRegular.ttf");
0349: oblmap.put("-lucidasans-bold", jreLibDirName
0350: + "/fonts/LucidaSansDemiBold.ttf");
0351: oblmap.put("-lucidatypewriter-medium", jreLibDirName
0352: + "/fonts/LucidaTypewriterRegular.ttf");
0353: oblmap.put("-lucidatypewriter-bold", jreLibDirName
0354: + "/fonts/LucidaTypewriterBold.ttf");
0355: }
0356:
0357: /**
0358: * Takes family name property in the following format:
0359: * "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1"
0360: * and returns the name of the corresponding physical font.
0361: * This code is used to resolve font configuration fonts, and expects
0362: * only to get called for these fonts.
0363: */
0364: public String getFileNameFromPlatformName(String platName) {
0365: String fileName = null;
0366: String fontID = specificFontIDForName(platName);
0367:
0368: /* If the font filename has been explicitly assigned in the
0369: * font configuration file, use it. This avoids accessing
0370: * the wrong fonts on Linux, where different fonts (some
0371: * of which may not be usable by 2D) may share the same
0372: * specific font ID. It may also speed up the lookup.
0373: */
0374: fileName = super .getFileNameFromPlatformName(platName);
0375: if (fileName != null) {
0376: if (isHeadless() && fileName.startsWith("-")) {
0377: /* if it's headless, no xlfd should be used */
0378: return null;
0379: }
0380: if (fileName.startsWith("/")) {
0381: /* If a path is assigned in the font configuration file,
0382: * it is required that the config file also specify using the
0383: * new awtfontpath key the X11 font directories
0384: * which must be added to the X11 font path to support
0385: * AWT access to that font. For that reason we no longer
0386: * have code here to add the parent directory to the list
0387: * of font config dirs, since the parent directory may not
0388: * be sufficient if fonts are symbolically linked to a
0389: * different directory.
0390: *
0391: * Add this XLFD (platform name) to the list of known
0392: * ones for this file.
0393: */
0394: Vector xVal = (Vector) xlfdMap.get(fileName);
0395: if (xVal == null) {
0396: /* Try to be robust on Linux distros which move fonts
0397: * around by verifying that the fileName represents a
0398: * file that exists. If it doesn't, set it to null
0399: * to trigger a search.
0400: */
0401: if (getFontConfiguration().needToSearchForFile(
0402: fileName)) {
0403: fileName = null;
0404: }
0405: if (fileName != null) {
0406: xVal = new Vector();
0407: xVal.add(platName);
0408: xlfdMap.put(fileName, xVal);
0409: }
0410: } else {
0411: if (!xVal.contains(platName)) {
0412: xVal.add(platName);
0413: }
0414: }
0415: }
0416: if (fileName != null) {
0417: fontNameMap.put(fontID, fileName);
0418: return fileName;
0419: }
0420: }
0421:
0422: if (fontID != null) {
0423: fileName = (String) fontNameMap.get(fontID);
0424: /* On Linux check for the Lucida Oblique fonts */
0425: if (fileName == null && isLinux && !isOpenJDK()) {
0426: if (oblmap == null) {
0427: initObliqueLucidaFontMap();
0428: }
0429: String oblkey = getObliqueLucidaFontID(fontID);
0430: if (oblkey != null) {
0431: fileName = oblmap.get(oblkey);
0432: }
0433: }
0434: if (fontPath == null
0435: && (fileName == null || !fileName.startsWith("/"))) {
0436: if (debugFonts) {
0437: logger
0438: .warning("** Registering all font paths because "
0439: + "can't find file for " + platName);
0440: }
0441: fontPath = getPlatformFontPath(noType1Font);
0442: registerFontDirs(fontPath);
0443: if (debugFonts) {
0444: logger
0445: .warning("** Finished registering all font paths");
0446: }
0447: fileName = (String) fontNameMap.get(fontID);
0448: }
0449: if (fileName == null && !isHeadless()) {
0450: /* Query X11 directly to see if this font is available
0451: * as a native font.
0452: */
0453: fileName = getX11FontName(platName);
0454: }
0455: if (fileName == null) {
0456: fontID = switchFontIDForName(platName);
0457: fileName = (String) fontNameMap.get(fontID);
0458: }
0459: if (fileName != null) {
0460: fontNameMap.put(fontID, fileName);
0461: }
0462: }
0463: return fileName;
0464: }
0465:
0466: private static String getX11FontName(String platName) {
0467: String xlfd = platName.replaceAll("%d", "*");
0468: if (NativeFont.fontExists(xlfd)) {
0469: return xlfd;
0470: } else {
0471: return null;
0472: }
0473: }
0474:
0475: /**
0476: * Returns the face name for the given XLFD.
0477: */
0478: public String getFileNameFromXLFD(String name) {
0479: String fileName = null;
0480: String fontID = specificFontIDForName(name);
0481: if (fontID != null) {
0482: fileName = (String) fontNameMap.get(fontID);
0483: if (fileName == null) {
0484: fontID = switchFontIDForName(name);
0485: fileName = (String) fontNameMap.get(fontID);
0486: }
0487: if (fileName == null) {
0488: fileName = getDefaultFontFile();
0489: }
0490: }
0491: return fileName;
0492: }
0493:
0494: // constants identifying XLFD and font ID fields
0495: private static final int FOUNDRY_FIELD = 1;
0496: private static final int FAMILY_NAME_FIELD = 2;
0497: private static final int WEIGHT_NAME_FIELD = 3;
0498: private static final int SLANT_FIELD = 4;
0499: private static final int SETWIDTH_NAME_FIELD = 5;
0500: private static final int ADD_STYLE_NAME_FIELD = 6;
0501: private static final int PIXEL_SIZE_FIELD = 7;
0502: private static final int POINT_SIZE_FIELD = 8;
0503: private static final int RESOLUTION_X_FIELD = 9;
0504: private static final int RESOLUTION_Y_FIELD = 10;
0505: private static final int SPACING_FIELD = 11;
0506: private static final int AVERAGE_WIDTH_FIELD = 12;
0507: private static final int CHARSET_REGISTRY_FIELD = 13;
0508: private static final int CHARSET_ENCODING_FIELD = 14;
0509:
0510: private String switchFontIDForName(String name) {
0511:
0512: int[] hPos = new int[14];
0513: int hyphenCnt = 1;
0514: int pos = 1;
0515:
0516: while (pos != -1 && hyphenCnt < 14) {
0517: pos = name.indexOf('-', pos);
0518: if (pos != -1) {
0519: hPos[hyphenCnt++] = pos;
0520: pos++;
0521: }
0522: }
0523:
0524: if (hyphenCnt != 14) {
0525: if (debugFonts) {
0526: logger
0527: .severe("Font Configuration Font ID is malformed:"
0528: + name);
0529: }
0530: return name; // what else can we do?
0531: }
0532:
0533: String slant = name.substring(hPos[SLANT_FIELD - 1] + 1,
0534: hPos[SLANT_FIELD]);
0535: String family = name.substring(hPos[FAMILY_NAME_FIELD - 1] + 1,
0536: hPos[FAMILY_NAME_FIELD]);
0537: String registry = name.substring(
0538: hPos[CHARSET_REGISTRY_FIELD - 1] + 1,
0539: hPos[CHARSET_REGISTRY_FIELD]);
0540: String encoding = name
0541: .substring(hPos[CHARSET_ENCODING_FIELD - 1] + 1);
0542:
0543: if (slant.equals("i")) {
0544: slant = "o";
0545: } else if (slant.equals("o")) {
0546: slant = "i";
0547: }
0548: // workaround for #4471000
0549: if (family.equals("itc zapfdingbats") && registry.equals("sun")
0550: && encoding.equals("fontspecific")) {
0551: registry = "adobe";
0552: }
0553: StringBuffer sb = new StringBuffer(name.substring(
0554: hPos[FAMILY_NAME_FIELD - 1], hPos[SLANT_FIELD - 1] + 1));
0555: sb.append(slant);
0556: sb.append(name.substring(hPos[SLANT_FIELD],
0557: hPos[SETWIDTH_NAME_FIELD] + 1));
0558: sb.append(registry);
0559: sb.append(name.substring(hPos[CHARSET_ENCODING_FIELD - 1]));
0560: String retval = sb.toString().toLowerCase(Locale.ENGLISH);
0561: return retval;
0562: }
0563:
0564: private String specificFontIDForName(String name) {
0565:
0566: int[] hPos = new int[14];
0567: int hyphenCnt = 1;
0568: int pos = 1;
0569:
0570: while (pos != -1 && hyphenCnt < 14) {
0571: pos = name.indexOf('-', pos);
0572: if (pos != -1) {
0573: hPos[hyphenCnt++] = pos;
0574: pos++;
0575: }
0576: }
0577:
0578: if (hyphenCnt != 14) {
0579: if (debugFonts) {
0580: logger
0581: .severe("Font Configuration Font ID is malformed:"
0582: + name);
0583: }
0584: return name; // what else can we do?
0585: }
0586:
0587: StringBuffer sb = new StringBuffer(name.substring(
0588: hPos[FAMILY_NAME_FIELD - 1], hPos[SETWIDTH_NAME_FIELD]));
0589: sb.append(name.substring(hPos[CHARSET_REGISTRY_FIELD - 1]));
0590: String retval = sb.toString().toLowerCase(Locale.ENGLISH);
0591: return retval;
0592: }
0593:
0594: protected String[] getNativeNames(String fontFileName,
0595: String platformName) {
0596: Vector nativeNames;
0597: if ((nativeNames = (Vector) xlfdMap.get(fontFileName)) == null) {
0598: if (platformName == null) {
0599: return null;
0600: } else {
0601: /* back-stop so that at least the name used in the
0602: * font configuration file is known as a native name
0603: */
0604: String[] natNames = new String[1];
0605: natNames[0] = platformName;
0606: return natNames;
0607: }
0608: } else {
0609: int len = nativeNames.size();
0610: return (String[]) nativeNames.toArray(new String[len]);
0611: }
0612: }
0613:
0614: // An X font spec (xlfd) includes an encoding. The same TrueType font file
0615: // may be referenced from different X font directories in font.dir files
0616: // to support use in multiple encodings by X apps.
0617: // So for the purposes of font configuration logical fonts where AWT
0618: // heavyweights need to access the font via X APIs we need to ensure that
0619: // the directory for precisely the encodings needed by this are added to
0620: // the x font path. This requires that we note the platform names
0621: // specified in font configuration files and use that to identify the
0622: // X font directory that contains a font.dir file for that platform name
0623: // and add it to the X font path (if display is local)
0624: // Here we make use of an already built map of xlfds to font locations
0625: // to add the font location to the set of those required to build the
0626: // x font path needed by AWT.
0627: // These are added to the x font path later.
0628: // All this is necessary because on Solaris the font.dir directories
0629: // may contain not real font files, but symbolic links to the actual
0630: // location but that location is not suitable for the x font path, since
0631: // it probably doesn't have a font.dir at all and certainly not one
0632: // with the required encodings
0633: // If the fontconfiguration file is properly set up so that all fonts
0634: // are mapped to files then we will never trigger initialising
0635: // xFontDirsMap (it will be null). In this case the awtfontpath entries
0636: // must specify all the X11 directories needed by AWT.
0637: protected void addFontToPlatformFontPath(String platformName) {
0638: if (xFontDirsMap != null) {
0639: String fontID = specificFontIDForName(platformName);
0640: String dirName = (String) xFontDirsMap.get(fontID);
0641: if (dirName != null) {
0642: fontConfigDirs.add(dirName);
0643: }
0644: }
0645: return;
0646: }
0647:
0648: protected void getPlatformFontPathFromFontConfig() {
0649: if (fontConfigDirs == null) {
0650: fontConfigDirs = getFontConfiguration().getAWTFontPathSet();
0651: if (debugFonts && fontConfigDirs != null) {
0652: String[] names = fontConfigDirs.toArray(new String[0]);
0653: for (int i = 0; i < names.length; i++) {
0654: logger.info("awtfontpath : " + names[i]);
0655: }
0656: }
0657: }
0658: }
0659:
0660: protected void registerPlatformFontsUsedByFontConfiguration() {
0661: if (fontConfigDirs == null) {
0662: return;
0663: }
0664: if (isLinux) {
0665: fontConfigDirs.add(jreLibDirName + File.separator
0666: + "oblique-fonts");
0667: }
0668: fontdirs = (String[]) fontConfigDirs.toArray(new String[0]);
0669: }
0670:
0671: /* Called by MToolkit to set the X11 font path */
0672: public static void setNativeFontPath() {
0673: if (fontdirs == null) {
0674: return;
0675: }
0676:
0677: // need to register these individually rather than by one call
0678: // to ensure that one bad directory doesn't cause all to be rejected
0679: for (int i = 0; i < fontdirs.length; i++) {
0680: if (debugFonts) {
0681: logger.info("Add " + fontdirs[i] + " to X11 fontpath");
0682: }
0683: FontManager.setNativeFontPath(fontdirs[i]);
0684: }
0685: }
0686:
0687: /* Register just the paths, (it doesn't register the fonts).
0688: * If a font configuration file has specified a baseFontPath
0689: * fontPath is just those directories, unless on usage we
0690: * find it doesn't contain what we need for the logical fonts.
0691: * Otherwise, we register all the paths on Solaris, because
0692: * the fontPath we have here is the complete one from
0693: * parsing /var/sadm/install/contents, not just
0694: * what's on the X font path (may be this should be
0695: * changed).
0696: * But for now what it means is that if we didn't do
0697: * this then if the font weren't listed anywhere on the
0698: * less complete font path we'd trigger loadFonts which
0699: * actually registers the fonts. This may actually be
0700: * the right thing tho' since that would also set up
0701: * the X font path without which we wouldn't be able to
0702: * display some "native" fonts.
0703: * So something to revisit is that probably fontPath
0704: * here ought to be only the X font path + jre font dir.
0705: * loadFonts should have a separate native call to
0706: * get the rest of the platform font path.
0707: *
0708: * Registering the directories can now be avoided in the
0709: * font configuration initialisation when filename entries
0710: * exist in the font configuration file for all fonts.
0711: * (Perhaps a little confusingly a filename entry is
0712: * actually keyed using the XLFD used in the font entries,
0713: * and it maps *to* a real filename).
0714: * In the event any are missing, registration of all
0715: * directories will be invoked to find the real files.
0716: *
0717: * But registering the directory performed other
0718: * functions such as filling in the map of all native names
0719: * for the font. So when this method isn't invoked, they still
0720: * must be found. This is mitigated by getNativeNames now
0721: * being able to return at least the platform name, but mostly
0722: * by ensuring that when a filename key is found, that
0723: * xlfd key is stored as one of the set of platform names
0724: * for the font. Its a set because typical font configuration
0725: * files reference the same CJK font files using multiple
0726: * X11 encodings. For the code that adds this to the map
0727: * see X11GE.getFileNameFromPlatformName(..)
0728: * If you don't get all of these then some code points may
0729: * not use the Xserver, and will not get the PCF bitmaps
0730: * that are available for some point sizes.
0731: * So, in the event that there is such a problem,
0732: * unconditionally making this call may be necessary, at
0733: * some cost to JRE start-up
0734: */
0735: protected void registerFontDirs(String pathName) {
0736:
0737: StringTokenizer parser = new StringTokenizer(pathName,
0738: File.pathSeparator);
0739: try {
0740: while (parser.hasMoreTokens()) {
0741: String dirPath = parser.nextToken();
0742: if (dirPath != null
0743: && !registeredDirs.containsKey(dirPath)) {
0744: registeredDirs.put(dirPath, null);
0745: registerFontDir(dirPath);
0746: }
0747: }
0748: } catch (NoSuchElementException e) {
0749: }
0750: }
0751:
0752: /* NOTE: this method needs to be executed in a privileged context.
0753: * The superclass constructor which is the primary caller of
0754: * this method executes entirely in such a context. Additionally
0755: * the loadFonts() method does too. So all should be well.
0756:
0757: */
0758: protected void registerFontDir(String path) {
0759: /* fonts.dir file format looks like :-
0760: * 47
0761: * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1
0762: * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1
0763: * ...
0764: */
0765: if (debugFonts) {
0766: logger.info("ParseFontDir " + path);
0767: }
0768: File fontsDotDir = new File(path + File.separator + "fonts.dir");
0769: FileReader fr = null;
0770: try {
0771: if (fontsDotDir.canRead()) {
0772: fr = new FileReader(fontsDotDir);
0773: BufferedReader br = new BufferedReader(fr, 8192);
0774: StreamTokenizer st = new StreamTokenizer(br);
0775: st.eolIsSignificant(true);
0776: int ttype = st.nextToken();
0777: if (ttype == StreamTokenizer.TT_NUMBER) {
0778: int numEntries = (int) st.nval;
0779: ttype = st.nextToken();
0780: if (ttype == StreamTokenizer.TT_EOL) {
0781: st.resetSyntax();
0782: st.wordChars(32, 127);
0783: st.wordChars(128 + 32, 255);
0784: st.whitespaceChars(0, 31);
0785:
0786: for (int i = 0; i < numEntries; i++) {
0787: ttype = st.nextToken();
0788: if (ttype == StreamTokenizer.TT_EOF) {
0789: break;
0790: }
0791: if (ttype != StreamTokenizer.TT_WORD) {
0792: break;
0793: }
0794: int breakPos = st.sval.indexOf(' ');
0795: if (breakPos <= 0) {
0796: /* On TurboLinux 8.0 a fonts.dir file had
0797: * a line with integer value "24" which
0798: * appeared to be the number of remaining
0799: * entries in the file. This didn't add to
0800: * the value on the first line of the file.
0801: * Seemed like XFree86 didn't like this line
0802: * much either. It failed to parse the file.
0803: * Ignore lines like this completely, and
0804: * don't let them count as an entry.
0805: */
0806: numEntries++;
0807: ttype = st.nextToken();
0808: if (ttype != StreamTokenizer.TT_EOL) {
0809: break;
0810: }
0811:
0812: continue;
0813: }
0814: if (st.sval.charAt(0) == '!') {
0815: /* TurboLinux 8.0 comment line: ignore.
0816: * can't use st.commentChar('!') to just
0817: * skip because this line mustn't count
0818: * against numEntries.
0819: */
0820: numEntries++;
0821: ttype = st.nextToken();
0822: if (ttype != StreamTokenizer.TT_EOL) {
0823: break;
0824: }
0825: continue;
0826: }
0827: String fileName = st.sval.substring(0,
0828: breakPos);
0829: /* TurboLinux 8.0 uses some additional syntax to
0830: * indicate algorithmic styling values.
0831: * Ignore ':' separated files at the beginning
0832: * of the fileName
0833: */
0834: int lastColon = fileName.lastIndexOf(':');
0835: if (lastColon > 0) {
0836: if (lastColon + 1 >= fileName.length()) {
0837: continue;
0838: }
0839: fileName = fileName
0840: .substring(lastColon + 1);
0841: }
0842: String fontPart = st.sval
0843: .substring(breakPos + 1);
0844: String fontID = specificFontIDForName(fontPart);
0845: String sVal = (String) fontNameMap
0846: .get(fontID);
0847:
0848: if (debugFonts) {
0849: logger.info("file=" + fileName
0850: + " xlfd=" + fontPart);
0851: logger.info("fontID=" + fontID
0852: + " sVal=" + sVal);
0853: }
0854: String fullPath = null;
0855: try {
0856: File file = new File(path, fileName);
0857: /* we may have a resolved symbolic link
0858: * this becomes important for an xlfd we
0859: * still need to know the location it was
0860: * found to update the X server font path
0861: * for use by AWT heavyweights - and when 2D
0862: * wants to use the native rasteriser.
0863: */
0864: if (xFontDirsMap == null) {
0865: xFontDirsMap = new HashMap();
0866: }
0867: xFontDirsMap.put(fontID, path);
0868: fullPath = file.getCanonicalPath();
0869: } catch (IOException e) {
0870: fullPath = path + File.separator
0871: + fileName;
0872: }
0873: Vector xVal = (Vector) xlfdMap
0874: .get(fullPath);
0875: if (debugFonts) {
0876: logger.info("fullPath=" + fullPath
0877: + " xVal=" + xVal);
0878: }
0879: if ((xVal == null || !xVal
0880: .contains(fontPart))
0881: && (sVal == null)
0882: || !sVal.startsWith("/")) {
0883: if (debugFonts) {
0884: logger.info("Map fontID:" + fontID
0885: + "to file:" + fullPath);
0886: }
0887: fontNameMap.put(fontID, fullPath);
0888: if (xVal == null) {
0889: xVal = new Vector();
0890: xlfdMap.put(fullPath, xVal);
0891: }
0892: xVal.add(fontPart);
0893: }
0894:
0895: ttype = st.nextToken();
0896: if (ttype != StreamTokenizer.TT_EOL) {
0897: break;
0898: }
0899: }
0900: }
0901: }
0902: fr.close();
0903: }
0904: } catch (IOException ioe1) {
0905: } finally {
0906: if (fr != null) {
0907: try {
0908: fr.close();
0909: } catch (IOException ioe2) {
0910: }
0911: }
0912: }
0913: }
0914:
0915: @Override
0916: public void loadFonts() {
0917: super .loadFonts();
0918: /* These maps are greatly expanded during a loadFonts but
0919: * can be reset to their initial state afterwards.
0920: * Since preferLocaleFonts() and preferProportionalFonts() will
0921: * trigger a partial repopulating from the FontConfiguration
0922: * it has to be the inital (empty) state for the latter two, not
0923: * simply nulling out.
0924: * xFontDirsMap is a special case in that the implementation
0925: * will typically not ever need to initialise it so it can be null.
0926: */
0927: xFontDirsMap = null;
0928: xlfdMap = new HashMap(1);
0929: fontNameMap = new HashMap(1);
0930: }
0931:
0932: // Implements SunGraphicsEnvironment.createFontConfiguration.
0933: protected FontConfiguration createFontConfiguration() {
0934: return new MFontConfiguration(this );
0935: }
0936:
0937: public FontConfiguration createFontConfiguration(
0938: boolean preferLocaleFonts, boolean preferPropFonts) {
0939:
0940: return new MFontConfiguration(this , preferLocaleFonts,
0941: preferPropFonts);
0942: }
0943:
0944: /**
0945: * Returns face name for default font, or null if
0946: * no face names are used for CompositeFontDescriptors
0947: * for this platform.
0948: */
0949: public String getDefaultFontFaceName() {
0950: return null;
0951: }
0952:
0953: private static native boolean pRunningXinerama();
0954:
0955: private static native Point getXineramaCenterPoint();
0956:
0957: /**
0958: * Override for Xinerama case: call new Solaris API for getting the correct
0959: * centering point from the windowing system.
0960: */
0961: public Point getCenterPoint() {
0962: if (runningXinerama()) {
0963: Point p = getXineramaCenterPoint();
0964: if (p != null) {
0965: return p;
0966: }
0967: }
0968: return super .getCenterPoint();
0969: }
0970:
0971: /**
0972: * Override for Xinerama case
0973: */
0974: public Rectangle getMaximumWindowBounds() {
0975: if (runningXinerama()) {
0976: return getXineramaWindowBounds();
0977: } else {
0978: return super .getMaximumWindowBounds();
0979: }
0980: }
0981:
0982: public boolean runningXinerama() {
0983: if (xinerState == null) {
0984: // pRunningXinerama() simply returns a global boolean variable,
0985: // so there is no need to synchronize here
0986: xinerState = Boolean.valueOf(pRunningXinerama());
0987: }
0988: return xinerState.booleanValue();
0989: }
0990:
0991: /**
0992: * Return the bounds for a centered Window on a system running in Xinerama
0993: * mode.
0994: *
0995: * Calculations are based on the assumption of a perfectly rectangular
0996: * display area (display edges line up with one another, and displays
0997: * have consistent width and/or height).
0998: *
0999: * The bounds to return depend on the arrangement of displays and on where
1000: * Windows are to be centered. There are two common situations:
1001: *
1002: * 1) The center point lies at the center of the combined area of all the
1003: * displays. In this case, the combined area of all displays is
1004: * returned.
1005: *
1006: * 2) The center point lies at the center of a single display. In this case
1007: * the user most likely wants centered Windows to be constrained to that
1008: * single display. The boundaries of the one display are returned.
1009: *
1010: * It is possible for the center point to be at both the center of the
1011: * entire display space AND at the center of a single monitor (a square of
1012: * 9 monitors, for instance). In this case, the entire display area is
1013: * returned.
1014: *
1015: * Because the center point is arbitrarily settable by the user, it could
1016: * fit neither of the cases above. The fallback case is to simply return
1017: * the combined area for all screens.
1018: */
1019: protected Rectangle getXineramaWindowBounds() {
1020: Point center = getCenterPoint();
1021: Rectangle unionRect, tempRect;
1022: GraphicsDevice[] gds = getScreenDevices();
1023: Rectangle centerMonitorRect = null;
1024: int i;
1025:
1026: // if center point is at the center of all monitors
1027: // return union of all bounds
1028: //
1029: // MM*MM MMM M
1030: // M*M *
1031: // MMM M
1032:
1033: // if center point is at center of a single monitor (but not of all
1034: // monitors)
1035: // return bounds of single monitor
1036: //
1037: // MMM MM
1038: // MM* *M
1039:
1040: // else, center is in some strange spot (such as on the border between
1041: // monitors), and we should just return the union of all monitors
1042: //
1043: // MM MMM
1044: // MM MMM
1045:
1046: unionRect = getUsableBounds(gds[0]);
1047:
1048: for (i = 0; i < gds.length; i++) {
1049: tempRect = getUsableBounds(gds[i]);
1050: if (centerMonitorRect == null
1051: &&
1052: // add a pixel or two for fudge-factor
1053: (tempRect.width / 2) + tempRect.x > center.x - 1
1054: && (tempRect.height / 2) + tempRect.y > center.y - 1
1055: && (tempRect.width / 2) + tempRect.x < center.x + 1
1056: && (tempRect.height / 2) + tempRect.y < center.y + 1) {
1057: centerMonitorRect = tempRect;
1058: }
1059: unionRect = unionRect.union(tempRect);
1060: }
1061:
1062: // first: check for center of all monitors (video wall)
1063: // add a pixel or two for fudge-factor
1064: if ((unionRect.width / 2) + unionRect.x > center.x - 1
1065: && (unionRect.height / 2) + unionRect.y > center.y - 1
1066: && (unionRect.width / 2) + unionRect.x < center.x + 1
1067: && (unionRect.height / 2) + unionRect.y < center.y + 1) {
1068:
1069: if (dbg.on) {
1070: dbg
1071: .println("Video Wall: center point is at center of all displays.");
1072: }
1073: return unionRect;
1074: }
1075:
1076: // next, check if at center of one monitor
1077: if (centerMonitorRect != null) {
1078: if (dbg.on) {
1079: dbg
1080: .println("Center point at center of a particular monitor, but not of the entire virtual display.");
1081: }
1082: return centerMonitorRect;
1083: }
1084:
1085: // otherwise, the center is at some weird spot: return unionRect
1086: if (dbg.on) {
1087: dbg
1088: .println("Center point is somewhere strange - return union of all bounds.");
1089: }
1090: return unionRect;
1091: }
1092:
1093: /**
1094: * From the DisplayChangedListener interface; devices do not need
1095: * to react to this event.
1096: */
1097: @Override
1098: public void paletteChanged() {
1099: }
1100: }
|