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