PageRenderTime 65ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/MRI-J/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java

http://github.com/GregBowyer/ManagedRuntimeInitiative
Java | 1067 lines | 618 code | 90 blank | 359 comment | 176 complexity | cdc058ff9c6ed47984aab5a982dd5772 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-3.0
  1. /*
  2. * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22. * CA 95054 USA or visit www.sun.com if you need additional information or
  23. * have any questions.
  24. */
  25. package sun.awt;
  26. import java.awt.GraphicsDevice;
  27. import java.awt.Point;
  28. import java.awt.Rectangle;
  29. import java.io.BufferedReader;
  30. import java.io.File;
  31. import java.io.FileReader;
  32. import java.io.FileNotFoundException;
  33. import java.io.InputStream;
  34. import java.io.IOException;
  35. import java.io.StreamTokenizer;
  36. import java.net.InetAddress;
  37. import java.net.NetworkInterface;
  38. import java.net.SocketException;
  39. import java.net.UnknownHostException;
  40. import java.util.*;
  41. import java.util.logging.*;
  42. import sun.awt.motif.MFontConfiguration;
  43. import sun.font.Font2D;
  44. import sun.font.FontManager;
  45. import sun.font.NativeFont;
  46. import sun.java2d.SunGraphicsEnvironment;
  47. /**
  48. * This is an implementation of a GraphicsEnvironment object for the
  49. * default local GraphicsEnvironment used by the Java Runtime Environment
  50. * for X11 environments.
  51. *
  52. * @see GraphicsDevice
  53. * @see GraphicsConfiguration
  54. */
  55. public class X11GraphicsEnvironment
  56. extends SunGraphicsEnvironment
  57. {
  58. private static final Logger log = Logger.getLogger("sun.awt.X11GraphicsEnvironment");
  59. private static final Logger screenLog = Logger.getLogger("sun.awt.screen.X11GraphicsEnvironment");
  60. private static Boolean xinerState;
  61. /*
  62. * This is the set of font directories needed to be on the X font path
  63. * to enable AWT heavyweights to find all of the font configuration fonts.
  64. * It is populated by :
  65. * - awtfontpath entries in the fontconfig.properties
  66. * - parent directories of "core" fonts used in the fontconfig.properties
  67. * - looking up font dirs in the xFontDirsMap where the key is a fontID
  68. * (cut down version of the XLFD read from the font configuration file).
  69. * This set is nulled out after use to free heap space.
  70. */
  71. private static HashSet<String> fontConfigDirs = null;
  72. /*
  73. * fontNameMap is a map from a fontID (which is a substring of an XLFD like
  74. * "-monotype-arial-bold-r-normal-iso8859-7")
  75. * to font file path like
  76. * /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf
  77. * It's used in a couple of methods like
  78. * getFileNameFomPlatformName(..) to help locate the font file.
  79. * We use this substring of a full XLFD because the font configuration files
  80. * define the XLFDs in a way that's easier to make into a request.
  81. * E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font
  82. * configuration files. We need to remove that part for comparisons.
  83. */
  84. private static Map fontNameMap = new HashMap();
  85. /* xFontDirsMap is also a map from a font ID to a font filepath.
  86. * The difference from fontNameMap is just that it does not have
  87. * resolved symbolic links. Normally this is not interesting except
  88. * that we need to know the directory in which a font was found to
  89. * add it to the X font server path, since although the files may
  90. * be linked, the fonts.dir is different and specific to the encoding
  91. * handled by that directory. This map is nulled out after use to free
  92. * heap space. If the optimal path is taken, such that all fonts in
  93. * font configuration files are referenced by filename, then the font
  94. * dir can be directly derived as its parent directory.
  95. * If a font is used by two XLFDs, each corresponding to a different
  96. * X11 font directory, then precautions must be taken to include both
  97. * directories.
  98. */
  99. private static Map xFontDirsMap;
  100. /*
  101. * xlfdMap is a map from a platform path like
  102. * /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like
  103. * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
  104. * Because there may be multiple native names, because the font is used
  105. * to support multiple X encodings for example, the value of an entry in
  106. * this map is always a vector where we store all the native names.
  107. * For fonts which we don't understand the key isn't a pathname, its
  108. * the full XLFD string like :-
  109. * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
  110. */
  111. private static Map xlfdMap = new HashMap();
  112. /*
  113. * Used to eliminate redundant work. When a font directory is
  114. * registered it added to this list. Subsequent registrations for the
  115. * same directory can then be skipped by checking this Map.
  116. * Access to this map is not synchronised here since creation
  117. * of the singleton GE instance is already synchronised and that is
  118. * the only code path that accesses this map.
  119. */
  120. private static HashMap registeredDirs = new HashMap();
  121. /* Array of directories to be added to the X11 font path.
  122. * Used by static method called from Toolkits which use X11 fonts.
  123. * Specifically this means MToolkit
  124. */
  125. private static String[] fontdirs = null;
  126. static {
  127. java.security.AccessController.doPrivileged(
  128. new java.security.PrivilegedAction() {
  129. public Object run() {
  130. System.loadLibrary("awt");
  131. /*
  132. * Note: The MToolkit object depends on the static initializer
  133. * of X11GraphicsEnvironment to initialize the connection to
  134. * the X11 server.
  135. */
  136. if (!isHeadless()) {
  137. // first check the OGL system property
  138. boolean glxRequested = false;
  139. String prop = System.getProperty("sun.java2d.opengl");
  140. if (prop != null) {
  141. if (prop.equals("true") || prop.equals("t")) {
  142. glxRequested = true;
  143. } else if (prop.equals("True") || prop.equals("T")) {
  144. glxRequested = true;
  145. glxVerbose = true;
  146. }
  147. }
  148. // initialize the X11 display connection
  149. initDisplay(glxRequested);
  150. // only attempt to initialize GLX if it was requested
  151. if (glxRequested) {
  152. glxAvailable = initGLX();
  153. if (glxVerbose && !glxAvailable) {
  154. System.out.println(
  155. "Could not enable OpenGL " +
  156. "pipeline (GLX 1.3 not available)");
  157. }
  158. }
  159. }
  160. return null;
  161. }
  162. });
  163. }
  164. private static boolean glxAvailable;
  165. private static boolean glxVerbose;
  166. private static native boolean initGLX();
  167. public static boolean isGLXAvailable() {
  168. return glxAvailable;
  169. }
  170. public static boolean isGLXVerbose() {
  171. return glxVerbose;
  172. }
  173. /**
  174. * Checks if Shared Memory extension can be used.
  175. * Returns:
  176. * -1 if server doesn't support MITShm
  177. * 1 if server supports it and it can be used
  178. * 0 otherwise
  179. */
  180. private static native int checkShmExt();
  181. private static native String getDisplayString();
  182. private static Boolean isDisplayLocal;
  183. /**
  184. * This should only be called from the static initializer, so no need for
  185. * the synchronized keyword.
  186. */
  187. private static native void initDisplay(boolean glxRequested);
  188. public X11GraphicsEnvironment() {
  189. }
  190. protected native int getNumScreens();
  191. protected GraphicsDevice makeScreenDevice(int screennum) {
  192. return new X11GraphicsDevice(screennum);
  193. }
  194. protected native int getDefaultScreenNum();
  195. /**
  196. * Returns the default screen graphics device.
  197. */
  198. public GraphicsDevice getDefaultScreenDevice() {
  199. return getScreenDevices()[getDefaultScreenNum()];
  200. }
  201. public static boolean isDisplayLocal() {
  202. if (isDisplayLocal == null) {
  203. SunToolkit.awtLock();
  204. try {
  205. if (isDisplayLocal == null) {
  206. isDisplayLocal = Boolean.valueOf(_isDisplayLocal());
  207. }
  208. } finally {
  209. SunToolkit.awtUnlock();
  210. }
  211. }
  212. return isDisplayLocal.booleanValue();
  213. }
  214. private static boolean _isDisplayLocal() {
  215. if (isHeadless()) {
  216. return true;
  217. }
  218. String isRemote = (String)java.security.AccessController.doPrivileged(
  219. new sun.security.action.GetPropertyAction("sun.java2d.remote"));
  220. if (isRemote != null) {
  221. return isRemote.equals("false");
  222. }
  223. int shm = checkShmExt();
  224. if (shm != -1) {
  225. return (shm == 1);
  226. }
  227. // If XServer doesn't support ShMem extension,
  228. // try the other way
  229. String display = getDisplayString();
  230. int ind = display.indexOf(':');
  231. final String hostName = display.substring(0, ind);
  232. if (ind <= 0) {
  233. // ':0' case
  234. return true;
  235. }
  236. Boolean result = (Boolean)java.security.AccessController.doPrivileged(
  237. new java.security.PrivilegedAction() {
  238. public Object run() {
  239. InetAddress remAddr[] = null;
  240. Enumeration locals = null;
  241. Enumeration interfaces = null;
  242. try {
  243. interfaces = NetworkInterface.getNetworkInterfaces();
  244. remAddr = InetAddress.getAllByName(hostName);
  245. if (remAddr == null) {
  246. return Boolean.FALSE;
  247. }
  248. } catch (UnknownHostException e) {
  249. System.err.println("Unknown host: " + hostName);
  250. return Boolean.FALSE;
  251. } catch (SocketException e1) {
  252. System.err.println(e1.getMessage());
  253. return Boolean.FALSE;
  254. }
  255. for (; interfaces.hasMoreElements();) {
  256. locals = ((NetworkInterface)interfaces.nextElement()).getInetAddresses();
  257. for (; locals.hasMoreElements();) {
  258. for (int i = 0; i < remAddr.length; i++) {
  259. if (locals.nextElement().equals(remAddr[i])) {
  260. return Boolean.TRUE;
  261. }
  262. }
  263. }
  264. }
  265. return Boolean.FALSE;
  266. }});
  267. return result.booleanValue();
  268. }
  269. /* These maps are used on Linux where we reference the Lucida oblique
  270. * fonts in fontconfig files even though they aren't in the standard
  271. * font directory. This explicitly remaps the XLFDs for these to the
  272. * correct base font. This is needed to prevent composite fonts from
  273. * defaulting to the Lucida Sans which is a bad substitute for the
  274. * monospaced Lucida Sans Typewriter. Also these maps prevent the
  275. * JRE from doing wasted work at start up.
  276. */
  277. HashMap<String, String> oblmap = null;
  278. private String getObliqueLucidaFontID(String fontID) {
  279. if (fontID.startsWith("-lucidasans-medium-i-normal") ||
  280. fontID.startsWith("-lucidasans-bold-i-normal") ||
  281. fontID.startsWith("-lucidatypewriter-medium-i-normal") ||
  282. fontID.startsWith("-lucidatypewriter-bold-i-normal")) {
  283. return fontID.substring(0, fontID.indexOf("-i-"));
  284. } else {
  285. return null;
  286. }
  287. }
  288. private void initObliqueLucidaFontMap() {
  289. oblmap = new HashMap<String, String>();
  290. oblmap.put("-lucidasans-medium",
  291. jreLibDirName+"/fonts/LucidaSansRegular.ttf");
  292. oblmap.put("-lucidasans-bold",
  293. jreLibDirName+"/fonts/LucidaSansDemiBold.ttf");
  294. oblmap.put("-lucidatypewriter-medium",
  295. jreLibDirName+"/fonts/LucidaTypewriterRegular.ttf");
  296. oblmap.put("-lucidatypewriter-bold",
  297. jreLibDirName+"/fonts/LucidaTypewriterBold.ttf");
  298. }
  299. /**
  300. * Takes family name property in the following format:
  301. * "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1"
  302. * and returns the name of the corresponding physical font.
  303. * This code is used to resolve font configuration fonts, and expects
  304. * only to get called for these fonts.
  305. */
  306. public String getFileNameFromPlatformName(String platName) {
  307. String fileName = null;
  308. String fontID = specificFontIDForName(platName);
  309. /* If the font filename has been explicitly assigned in the
  310. * font configuration file, use it. This avoids accessing
  311. * the wrong fonts on Linux, where different fonts (some
  312. * of which may not be usable by 2D) may share the same
  313. * specific font ID. It may also speed up the lookup.
  314. */
  315. fileName = super.getFileNameFromPlatformName(platName);
  316. if (fileName != null) {
  317. if (isHeadless() && fileName.startsWith("-")) {
  318. /* if it's headless, no xlfd should be used */
  319. return null;
  320. }
  321. if (fileName.startsWith("/")) {
  322. /* If a path is assigned in the font configuration file,
  323. * it is required that the config file also specify using the
  324. * new awtfontpath key the X11 font directories
  325. * which must be added to the X11 font path to support
  326. * AWT access to that font. For that reason we no longer
  327. * have code here to add the parent directory to the list
  328. * of font config dirs, since the parent directory may not
  329. * be sufficient if fonts are symbolically linked to a
  330. * different directory.
  331. *
  332. * Add this XLFD (platform name) to the list of known
  333. * ones for this file.
  334. */
  335. Vector xVal = (Vector) xlfdMap.get(fileName);
  336. if (xVal == null) {
  337. /* Try to be robust on Linux distros which move fonts
  338. * around by verifying that the fileName represents a
  339. * file that exists. If it doesn't, set it to null
  340. * to trigger a search.
  341. */
  342. if (getFontConfiguration().needToSearchForFile(fileName)) {
  343. fileName = null;
  344. }
  345. if (fileName != null) {
  346. xVal = new Vector();
  347. xVal.add(platName);
  348. xlfdMap.put(fileName, xVal);
  349. }
  350. } else {
  351. if (!xVal.contains(platName)) {
  352. xVal.add(platName);
  353. }
  354. }
  355. }
  356. if (fileName != null) {
  357. fontNameMap.put(fontID, fileName);
  358. return fileName;
  359. }
  360. }
  361. if (fontID != null) {
  362. fileName = (String)fontNameMap.get(fontID);
  363. /* On Linux check for the Lucida Oblique fonts */
  364. if (fileName == null && isLinux && !isOpenJDK()) {
  365. if (oblmap == null) {
  366. initObliqueLucidaFontMap();
  367. }
  368. String oblkey = getObliqueLucidaFontID(fontID);
  369. if (oblkey != null) {
  370. fileName = oblmap.get(oblkey);
  371. }
  372. }
  373. if (fontPath == null &&
  374. (fileName == null || !fileName.startsWith("/"))) {
  375. if (debugFonts) {
  376. logger.warning("** Registering all font paths because " +
  377. "can't find file for " + platName);
  378. }
  379. fontPath = getPlatformFontPath(noType1Font);
  380. registerFontDirs(fontPath);
  381. if (debugFonts) {
  382. logger.warning("** Finished registering all font paths");
  383. }
  384. fileName = (String)fontNameMap.get(fontID);
  385. }
  386. if (fileName == null && !isHeadless()) {
  387. /* Query X11 directly to see if this font is available
  388. * as a native font.
  389. */
  390. fileName = getX11FontName(platName);
  391. }
  392. if (fileName == null) {
  393. fontID = switchFontIDForName(platName);
  394. fileName = (String)fontNameMap.get(fontID);
  395. }
  396. if (fileName != null) {
  397. fontNameMap.put(fontID, fileName);
  398. }
  399. }
  400. return fileName;
  401. }
  402. private static String getX11FontName(String platName) {
  403. String xlfd = platName.replaceAll("%d", "*");
  404. if (NativeFont.fontExists(xlfd)) {
  405. return xlfd;
  406. } else {
  407. return null;
  408. }
  409. }
  410. /**
  411. * Returns the face name for the given XLFD.
  412. */
  413. public String getFileNameFromXLFD(String name) {
  414. String fileName = null;
  415. String fontID = specificFontIDForName(name);
  416. if (fontID != null) {
  417. fileName = (String)fontNameMap.get(fontID);
  418. if (fileName == null) {
  419. fontID = switchFontIDForName(name);
  420. fileName = (String)fontNameMap.get(fontID);
  421. }
  422. if (fileName == null) {
  423. fileName = getDefaultFontFile();
  424. }
  425. }
  426. return fileName;
  427. }
  428. // constants identifying XLFD and font ID fields
  429. private static final int FOUNDRY_FIELD = 1;
  430. private static final int FAMILY_NAME_FIELD = 2;
  431. private static final int WEIGHT_NAME_FIELD = 3;
  432. private static final int SLANT_FIELD = 4;
  433. private static final int SETWIDTH_NAME_FIELD = 5;
  434. private static final int ADD_STYLE_NAME_FIELD = 6;
  435. private static final int PIXEL_SIZE_FIELD = 7;
  436. private static final int POINT_SIZE_FIELD = 8;
  437. private static final int RESOLUTION_X_FIELD = 9;
  438. private static final int RESOLUTION_Y_FIELD = 10;
  439. private static final int SPACING_FIELD = 11;
  440. private static final int AVERAGE_WIDTH_FIELD = 12;
  441. private static final int CHARSET_REGISTRY_FIELD = 13;
  442. private static final int CHARSET_ENCODING_FIELD = 14;
  443. private String switchFontIDForName(String name) {
  444. int[] hPos = new int[14];
  445. int hyphenCnt = 1;
  446. int pos = 1;
  447. while (pos != -1 && hyphenCnt < 14) {
  448. pos = name.indexOf('-', pos);
  449. if (pos != -1) {
  450. hPos[hyphenCnt++] = pos;
  451. pos++;
  452. }
  453. }
  454. if (hyphenCnt != 14) {
  455. if (debugFonts) {
  456. logger.severe("Font Configuration Font ID is malformed:" + name);
  457. }
  458. return name; // what else can we do?
  459. }
  460. String slant = name.substring(hPos[SLANT_FIELD-1]+1,
  461. hPos[SLANT_FIELD]);
  462. String family = name.substring(hPos[FAMILY_NAME_FIELD-1]+1,
  463. hPos[FAMILY_NAME_FIELD]);
  464. String registry = name.substring(hPos[CHARSET_REGISTRY_FIELD-1]+1,
  465. hPos[CHARSET_REGISTRY_FIELD]);
  466. String encoding = name.substring(hPos[CHARSET_ENCODING_FIELD-1]+1);
  467. if (slant.equals("i")) {
  468. slant = "o";
  469. } else if (slant.equals("o")) {
  470. slant = "i";
  471. }
  472. // workaround for #4471000
  473. if (family.equals("itc zapfdingbats")
  474. && registry.equals("sun")
  475. && encoding.equals("fontspecific")){
  476. registry = "adobe";
  477. }
  478. StringBuffer sb =
  479. new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
  480. hPos[SLANT_FIELD-1]+1));
  481. sb.append(slant);
  482. sb.append(name.substring(hPos[SLANT_FIELD],
  483. hPos[SETWIDTH_NAME_FIELD]+1));
  484. sb.append(registry);
  485. sb.append(name.substring(hPos[CHARSET_ENCODING_FIELD-1]));
  486. String retval = sb.toString().toLowerCase (Locale.ENGLISH);
  487. return retval;
  488. }
  489. private String specificFontIDForName(String name) {
  490. int[] hPos = new int[14];
  491. int hyphenCnt = 1;
  492. int pos = 1;
  493. while (pos != -1 && hyphenCnt < 14) {
  494. pos = name.indexOf('-', pos);
  495. if (pos != -1) {
  496. hPos[hyphenCnt++] = pos;
  497. pos++;
  498. }
  499. }
  500. if (hyphenCnt != 14) {
  501. if (debugFonts) {
  502. logger.severe("Font Configuration Font ID is malformed:" + name);
  503. }
  504. return name; // what else can we do?
  505. }
  506. StringBuffer sb =
  507. new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
  508. hPos[SETWIDTH_NAME_FIELD]));
  509. sb.append(name.substring(hPos[CHARSET_REGISTRY_FIELD-1]));
  510. String retval = sb.toString().toLowerCase (Locale.ENGLISH);
  511. return retval;
  512. }
  513. protected String[] getNativeNames(String fontFileName,
  514. String platformName) {
  515. Vector nativeNames;
  516. if ((nativeNames=(Vector)xlfdMap.get(fontFileName))==null) {
  517. if (platformName == null) {
  518. return null;
  519. } else {
  520. /* back-stop so that at least the name used in the
  521. * font configuration file is known as a native name
  522. */
  523. String []natNames = new String[1];
  524. natNames[0] = platformName;
  525. return natNames;
  526. }
  527. } else {
  528. int len = nativeNames.size();
  529. return (String[])nativeNames.toArray(new String[len]);
  530. }
  531. }
  532. // An X font spec (xlfd) includes an encoding. The same TrueType font file
  533. // may be referenced from different X font directories in font.dir files
  534. // to support use in multiple encodings by X apps.
  535. // So for the purposes of font configuration logical fonts where AWT
  536. // heavyweights need to access the font via X APIs we need to ensure that
  537. // the directory for precisely the encodings needed by this are added to
  538. // the x font path. This requires that we note the platform names
  539. // specified in font configuration files and use that to identify the
  540. // X font directory that contains a font.dir file for that platform name
  541. // and add it to the X font path (if display is local)
  542. // Here we make use of an already built map of xlfds to font locations
  543. // to add the font location to the set of those required to build the
  544. // x font path needed by AWT.
  545. // These are added to the x font path later.
  546. // All this is necessary because on Solaris the font.dir directories
  547. // may contain not real font files, but symbolic links to the actual
  548. // location but that location is not suitable for the x font path, since
  549. // it probably doesn't have a font.dir at all and certainly not one
  550. // with the required encodings
  551. // If the fontconfiguration file is properly set up so that all fonts
  552. // are mapped to files then we will never trigger initialising
  553. // xFontDirsMap (it will be null). In this case the awtfontpath entries
  554. // must specify all the X11 directories needed by AWT.
  555. protected void addFontToPlatformFontPath(String platformName) {
  556. if (xFontDirsMap != null) {
  557. String fontID = specificFontIDForName(platformName);
  558. String dirName = (String)xFontDirsMap.get(fontID);
  559. if (dirName != null) {
  560. fontConfigDirs.add(dirName);
  561. }
  562. }
  563. return;
  564. }
  565. protected void getPlatformFontPathFromFontConfig() {
  566. if (fontConfigDirs == null) {
  567. fontConfigDirs = getFontConfiguration().getAWTFontPathSet();
  568. if (debugFonts && fontConfigDirs != null) {
  569. String[] names = fontConfigDirs.toArray(new String[0]);
  570. for (int i=0;i<names.length;i++) {
  571. logger.info("awtfontpath : " + names[i]);
  572. }
  573. }
  574. }
  575. }
  576. protected void registerPlatformFontsUsedByFontConfiguration() {
  577. if (fontConfigDirs == null) {
  578. return;
  579. }
  580. if (isLinux) {
  581. fontConfigDirs.add(jreLibDirName+File.separator+"oblique-fonts");
  582. }
  583. fontdirs = (String[])fontConfigDirs.toArray(new String[0]);
  584. }
  585. /* Called by MToolkit to set the X11 font path */
  586. public static void setNativeFontPath() {
  587. if (fontdirs == null) {
  588. return;
  589. }
  590. // need to register these individually rather than by one call
  591. // to ensure that one bad directory doesn't cause all to be rejected
  592. for (int i=0; i<fontdirs.length; i++) {
  593. if (debugFonts) {
  594. logger.info("Add " + fontdirs[i] + " to X11 fontpath");
  595. }
  596. FontManager.setNativeFontPath(fontdirs[i]);
  597. }
  598. }
  599. /* Register just the paths, (it doesn't register the fonts).
  600. * If a font configuration file has specified a baseFontPath
  601. * fontPath is just those directories, unless on usage we
  602. * find it doesn't contain what we need for the logical fonts.
  603. * Otherwise, we register all the paths on Solaris, because
  604. * the fontPath we have here is the complete one from
  605. * parsing /var/sadm/install/contents, not just
  606. * what's on the X font path (may be this should be
  607. * changed).
  608. * But for now what it means is that if we didn't do
  609. * this then if the font weren't listed anywhere on the
  610. * less complete font path we'd trigger loadFonts which
  611. * actually registers the fonts. This may actually be
  612. * the right thing tho' since that would also set up
  613. * the X font path without which we wouldn't be able to
  614. * display some "native" fonts.
  615. * So something to revisit is that probably fontPath
  616. * here ought to be only the X font path + jre font dir.
  617. * loadFonts should have a separate native call to
  618. * get the rest of the platform font path.
  619. *
  620. * Registering the directories can now be avoided in the
  621. * font configuration initialisation when filename entries
  622. * exist in the font configuration file for all fonts.
  623. * (Perhaps a little confusingly a filename entry is
  624. * actually keyed using the XLFD used in the font entries,
  625. * and it maps *to* a real filename).
  626. * In the event any are missing, registration of all
  627. * directories will be invoked to find the real files.
  628. *
  629. * But registering the directory performed other
  630. * functions such as filling in the map of all native names
  631. * for the font. So when this method isn't invoked, they still
  632. * must be found. This is mitigated by getNativeNames now
  633. * being able to return at least the platform name, but mostly
  634. * by ensuring that when a filename key is found, that
  635. * xlfd key is stored as one of the set of platform names
  636. * for the font. Its a set because typical font configuration
  637. * files reference the same CJK font files using multiple
  638. * X11 encodings. For the code that adds this to the map
  639. * see X11GE.getFileNameFromPlatformName(..)
  640. * If you don't get all of these then some code points may
  641. * not use the Xserver, and will not get the PCF bitmaps
  642. * that are available for some point sizes.
  643. * So, in the event that there is such a problem,
  644. * unconditionally making this call may be necessary, at
  645. * some cost to JRE start-up
  646. */
  647. protected void registerFontDirs(String pathName) {
  648. StringTokenizer parser = new StringTokenizer(pathName,
  649. File.pathSeparator);
  650. try {
  651. while (parser.hasMoreTokens()) {
  652. String dirPath = parser.nextToken();
  653. if (dirPath != null && !registeredDirs.containsKey(dirPath)) {
  654. registeredDirs.put(dirPath, null);
  655. registerFontDir(dirPath);
  656. }
  657. }
  658. } catch (NoSuchElementException e) {
  659. }
  660. }
  661. /* NOTE: this method needs to be executed in a privileged context.
  662. * The superclass constructor which is the primary caller of
  663. * this method executes entirely in such a context. Additionally
  664. * the loadFonts() method does too. So all should be well.
  665. */
  666. protected void registerFontDir(String path) {
  667. /* fonts.dir file format looks like :-
  668. * 47
  669. * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1
  670. * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1
  671. * ...
  672. */
  673. if (debugFonts) {
  674. logger.info("ParseFontDir " + path);
  675. }
  676. File fontsDotDir = new File(path + File.separator + "fonts.dir");
  677. FileReader fr = null;
  678. try {
  679. if (fontsDotDir.canRead()) {
  680. fr = new FileReader(fontsDotDir);
  681. BufferedReader br = new BufferedReader(fr, 8192);
  682. StreamTokenizer st = new StreamTokenizer(br);
  683. st.eolIsSignificant(true);
  684. int ttype = st.nextToken();
  685. if (ttype == StreamTokenizer.TT_NUMBER) {
  686. int numEntries = (int)st.nval;
  687. ttype = st.nextToken();
  688. if (ttype == StreamTokenizer.TT_EOL) {
  689. st.resetSyntax();
  690. st.wordChars(32, 127);
  691. st.wordChars(128 + 32, 255);
  692. st.whitespaceChars(0, 31);
  693. for (int i=0; i < numEntries; i++) {
  694. ttype = st.nextToken();
  695. if (ttype == StreamTokenizer.TT_EOF) {
  696. break;
  697. }
  698. if (ttype != StreamTokenizer.TT_WORD) {
  699. break;
  700. }
  701. int breakPos = st.sval.indexOf(' ');
  702. if (breakPos <= 0) {
  703. /* On TurboLinux 8.0 a fonts.dir file had
  704. * a line with integer value "24" which
  705. * appeared to be the number of remaining
  706. * entries in the file. This didn't add to
  707. * the value on the first line of the file.
  708. * Seemed like XFree86 didn't like this line
  709. * much either. It failed to parse the file.
  710. * Ignore lines like this completely, and
  711. * don't let them count as an entry.
  712. */
  713. numEntries++;
  714. ttype = st.nextToken();
  715. if (ttype != StreamTokenizer.TT_EOL) {
  716. break;
  717. }
  718. continue;
  719. }
  720. if (st.sval.charAt(0) == '!') {
  721. /* TurboLinux 8.0 comment line: ignore.
  722. * can't use st.commentChar('!') to just
  723. * skip because this line mustn't count
  724. * against numEntries.
  725. */
  726. numEntries++;
  727. ttype = st.nextToken();
  728. if (ttype != StreamTokenizer.TT_EOL) {
  729. break;
  730. }
  731. continue;
  732. }
  733. String fileName = st.sval.substring(0, breakPos);
  734. /* TurboLinux 8.0 uses some additional syntax to
  735. * indicate algorithmic styling values.
  736. * Ignore ':' separated files at the beginning
  737. * of the fileName
  738. */
  739. int lastColon = fileName.lastIndexOf(':');
  740. if (lastColon > 0) {
  741. if (lastColon+1 >= fileName.length()) {
  742. continue;
  743. }
  744. fileName = fileName.substring(lastColon+1);
  745. }
  746. String fontPart = st.sval.substring(breakPos+1);
  747. String fontID = specificFontIDForName(fontPart);
  748. String sVal = (String) fontNameMap.get(fontID);
  749. if (debugFonts) {
  750. logger.info("file=" + fileName +
  751. " xlfd=" + fontPart);
  752. logger.info("fontID=" + fontID +
  753. " sVal=" + sVal);
  754. }
  755. String fullPath = null;
  756. try {
  757. File file = new File(path,fileName);
  758. /* we may have a resolved symbolic link
  759. * this becomes important for an xlfd we
  760. * still need to know the location it was
  761. * found to update the X server font path
  762. * for use by AWT heavyweights - and when 2D
  763. * wants to use the native rasteriser.
  764. */
  765. if (xFontDirsMap == null) {
  766. xFontDirsMap = new HashMap();
  767. }
  768. xFontDirsMap.put(fontID, path);
  769. fullPath = file.getCanonicalPath();
  770. } catch (IOException e) {
  771. fullPath = path + File.separator + fileName;
  772. }
  773. Vector xVal = (Vector) xlfdMap.get(fullPath);
  774. if (debugFonts) {
  775. logger.info("fullPath=" + fullPath +
  776. " xVal=" + xVal);
  777. }
  778. if ((xVal == null || !xVal.contains(fontPart)) &&
  779. (sVal == null) || !sVal.startsWith("/")) {
  780. if (debugFonts) {
  781. logger.info("Map fontID:"+fontID +
  782. "to file:" + fullPath);
  783. }
  784. fontNameMap.put(fontID, fullPath);
  785. if (xVal == null) {
  786. xVal = new Vector();
  787. xlfdMap.put (fullPath, xVal);
  788. }
  789. xVal.add(fontPart);
  790. }
  791. ttype = st.nextToken();
  792. if (ttype != StreamTokenizer.TT_EOL) {
  793. break;
  794. }
  795. }
  796. }
  797. }
  798. fr.close();
  799. }
  800. } catch (IOException ioe1) {
  801. } finally {
  802. if (fr != null) {
  803. try {
  804. fr.close();
  805. } catch (IOException ioe2) {
  806. }
  807. }
  808. }
  809. }
  810. @Override
  811. public void loadFonts() {
  812. super.loadFonts();
  813. /* These maps are greatly expanded during a loadFonts but
  814. * can be reset to their initial state afterwards.
  815. * Since preferLocaleFonts() and preferProportionalFonts() will
  816. * trigger a partial repopulating from the FontConfiguration
  817. * it has to be the inital (empty) state for the latter two, not
  818. * simply nulling out.
  819. * xFontDirsMap is a special case in that the implementation
  820. * will typically not ever need to initialise it so it can be null.
  821. */
  822. xFontDirsMap = null;
  823. xlfdMap = new HashMap(1);
  824. fontNameMap = new HashMap(1);
  825. }
  826. // Implements SunGraphicsEnvironment.createFontConfiguration.
  827. protected FontConfiguration createFontConfiguration() {
  828. return new MFontConfiguration(this);
  829. }
  830. public FontConfiguration
  831. createFontConfiguration(boolean preferLocaleFonts,
  832. boolean preferPropFonts) {
  833. return new MFontConfiguration(this,
  834. preferLocaleFonts, preferPropFonts);
  835. }
  836. /**
  837. * Returns face name for default font, or null if
  838. * no face names are used for CompositeFontDescriptors
  839. * for this platform.
  840. */
  841. public String getDefaultFontFaceName() {
  842. return null;
  843. }
  844. private static native boolean pRunningXinerama();
  845. private static native Point getXineramaCenterPoint();
  846. /**
  847. * Override for Xinerama case: call new Solaris API for getting the correct
  848. * centering point from the windowing system.
  849. */
  850. public Point getCenterPoint() {
  851. if (runningXinerama()) {
  852. Point p = getXineramaCenterPoint();
  853. if (p != null) {
  854. return p;
  855. }
  856. }
  857. return super.getCenterPoint();
  858. }
  859. /**
  860. * Override for Xinerama case
  861. */
  862. public Rectangle getMaximumWindowBounds() {
  863. if (runningXinerama()) {
  864. return getXineramaWindowBounds();
  865. } else {
  866. return super.getMaximumWindowBounds();
  867. }
  868. }
  869. public boolean runningXinerama() {
  870. if (xinerState == null) {
  871. // pRunningXinerama() simply returns a global boolean variable,
  872. // so there is no need to synchronize here
  873. xinerState = Boolean.valueOf(pRunningXinerama());
  874. if (screenLog.isLoggable(Level.FINER)) {
  875. screenLog.log(Level.FINER, "Running Xinerama: " + xinerState);
  876. }
  877. }
  878. return xinerState.booleanValue();
  879. }
  880. /**
  881. * Return the bounds for a centered Window on a system running in Xinerama
  882. * mode.
  883. *
  884. * Calculations are based on the assumption of a perfectly rectangular
  885. * display area (display edges line up with one another, and displays
  886. * have consistent width and/or height).
  887. *
  888. * The bounds to return depend on the arrangement of displays and on where
  889. * Windows are to be centered. There are two common situations:
  890. *
  891. * 1) The center point lies at the center of the combined area of all the
  892. * displays. In this case, the combined area of all displays is
  893. * returned.
  894. *
  895. * 2) The center point lies at the center of a single display. In this case
  896. * the user most likely wants centered Windows to be constrained to that
  897. * single display. The boundaries of the one display are returned.
  898. *
  899. * It is possible for the center point to be at both the center of the
  900. * entire display space AND at the center of a single monitor (a square of
  901. * 9 monitors, for instance). In this case, the entire display area is
  902. * returned.
  903. *
  904. * Because the center point is arbitrarily settable by the user, it could
  905. * fit neither of the cases above. The fallback case is to simply return
  906. * the combined area for all screens.
  907. */
  908. protected Rectangle getXineramaWindowBounds() {
  909. Point center = getCenterPoint();
  910. Rectangle unionRect, tempRect;
  911. GraphicsDevice[] gds = getScreenDevices();
  912. Rectangle centerMonitorRect = null;
  913. int i;
  914. // if center point is at the center of all monitors
  915. // return union of all bounds
  916. //
  917. // MM*MM MMM M
  918. // M*M *
  919. // MMM M
  920. // if center point is at center of a single monitor (but not of all
  921. // monitors)
  922. // return bounds of single monitor
  923. //
  924. // MMM MM
  925. // MM* *M
  926. // else, center is in some strange spot (such as on the border between
  927. // monitors), and we should just return the union of all monitors
  928. //
  929. // MM MMM
  930. // MM MMM
  931. unionRect = getUsableBounds(gds[0]);
  932. for (i = 0; i < gds.length; i++) {
  933. tempRect = getUsableBounds(gds[i]);
  934. if (centerMonitorRect == null &&
  935. // add a pixel or two for fudge-factor
  936. (tempRect.width / 2) + tempRect.x > center.x - 1 &&
  937. (tempRect.height / 2) + tempRect.y > center.y - 1 &&
  938. (tempRect.width / 2) + tempRect.x < center.x + 1 &&
  939. (tempRect.height / 2) + tempRect.y < center.y + 1) {
  940. centerMonitorRect = tempRect;
  941. }
  942. unionRect = unionRect.union(tempRect);
  943. }
  944. // first: check for center of all monitors (video wall)
  945. // add a pixel or two for fudge-factor
  946. if ((unionRect.width / 2) + unionRect.x > center.x - 1 &&
  947. (unionRect.height / 2) + unionRect.y > center.y - 1 &&
  948. (unionRect.width / 2) + unionRect.x < center.x + 1 &&
  949. (unionRect.height / 2) + unionRect.y < center.y + 1) {
  950. if (screenLog.isLoggable(Level.FINER)) {
  951. screenLog.log(Level.FINER, "Video Wall: center point is at center of all displays.");
  952. }
  953. return unionRect;
  954. }
  955. // next, check if at center of one monitor
  956. if (centerMonitorRect != null) {
  957. if (screenLog.isLoggable(Level.FINER)) {
  958. screenLog.log(Level.FINER, "Center point at center of a particular " +
  959. "monitor, but not of the entire virtual display.");
  960. }
  961. return centerMonitorRect;
  962. }
  963. // otherwise, the center is at some weird spot: return unionRect
  964. if (screenLog.isLoggable(Level.FINER)) {
  965. screenLog.log(Level.FINER, "Center point is somewhere strange - return union of all bounds.");
  966. }
  967. return unionRect;
  968. }
  969. /**
  970. * From the DisplayChangedListener interface; devices do not need
  971. * to react to this event.
  972. */
  973. @Override
  974. public void paletteChanged() {
  975. }
  976. }