1 /* 2 * Copyright 2001-2016, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 /*! Manages font families and styles */ 12 13 14 #include "GlobalFontManager.h" 15 16 #include <new> 17 18 #include <Autolock.h> 19 #include <Debug.h> 20 #include <Directory.h> 21 #include <Entry.h> 22 #include <File.h> 23 #include <FindDirectory.h> 24 #include <Message.h> 25 #include <NodeMonitor.h> 26 #include <Path.h> 27 #include <String.h> 28 29 #include <ft2build.h> 30 #include FT_FREETYPE_H 31 32 #include "FontFamily.h" 33 #include "ServerConfig.h" 34 #include "ServerFont.h" 35 36 37 //#define TRACE_GLOBAL_FONT_MANAGER 38 #ifdef TRACE_GLOBAL_FONT_MANAGER 39 # define FTRACE(x) debug_printf x 40 #else 41 # define FTRACE(x) ; 42 #endif 43 44 45 // TODO: needs some more work for multi-user support 46 47 GlobalFontManager* gFontManager = NULL; 48 extern FT_Library gFreeTypeLibrary; 49 50 51 struct GlobalFontManager::font_directory { 52 node_ref directory; 53 uid_t user; 54 gid_t group; 55 uint32 revision; 56 BObjectList<FontStyle> styles; 57 58 bool AlreadyScanned() const { return revision != 0; } 59 FontStyle* FindStyle(const node_ref& nodeRef) const; 60 }; 61 62 63 struct GlobalFontManager::font_mapping { 64 BString family; 65 BString style; 66 entry_ref ref; 67 }; 68 69 70 FontStyle* 71 GlobalFontManager::font_directory::FindStyle(const node_ref& nodeRef) const 72 { 73 for (int32 i = styles.CountItems(); i-- > 0;) { 74 FontStyle* style = styles.ItemAt(i); 75 76 if (nodeRef == style->NodeRef()) 77 return style; 78 } 79 80 return NULL; 81 } 82 83 84 static status_t 85 set_entry(node_ref& nodeRef, const char* name, BEntry& entry) 86 { 87 entry_ref ref; 88 ref.device = nodeRef.device; 89 ref.directory = nodeRef.node; 90 91 status_t status = ref.set_name(name); 92 if (status != B_OK) 93 return status; 94 95 return entry.SetTo(&ref); 96 } 97 98 99 // #pragma mark - 100 101 102 //! Does basic set up so that directories can be scanned 103 GlobalFontManager::GlobalFontManager() 104 : FontManagerBase(true, "GlobalFontManager"), 105 fDirectories(10, true), 106 fMappings(10, true), 107 108 fDefaultPlainFont(NULL), 109 fDefaultBoldFont(NULL), 110 fDefaultFixedFont(NULL), 111 112 fScanned(false) 113 { 114 if (InitCheck() == B_OK) { 115 _AddSystemPaths(); 116 _LoadRecentFontMappings(); 117 118 status_t status = _SetDefaultFonts(); 119 120 if (status == B_OK) { 121 // Precache the plain and bold fonts 122 _PrecacheFontFile(fDefaultPlainFont.Get()); 123 _PrecacheFontFile(fDefaultBoldFont.Get()); 124 125 // Post a message so we scan the initial paths. 126 PostMessage(B_PULSE); 127 } 128 129 SetInitStatus(status); 130 } 131 } 132 133 134 //! Frees items allocated in the constructor and deletes all global families 135 GlobalFontManager::~GlobalFontManager() 136 { 137 fDefaultPlainFont.Unset(); 138 fDefaultBoldFont.Unset(); 139 fDefaultFixedFont.Unset(); 140 } 141 142 143 void 144 GlobalFontManager::MessageReceived(BMessage* message) 145 { 146 switch (message->what) { 147 case B_NODE_MONITOR: 148 { 149 int32 opcode; 150 if (message->FindInt32("opcode", &opcode) != B_OK) 151 return; 152 153 switch (opcode) { 154 case B_ENTRY_CREATED: 155 { 156 const char* name; 157 node_ref nodeRef; 158 if (message->FindInt32("device", &nodeRef.device) != B_OK 159 || message->FindInt64("directory", &nodeRef.node) != B_OK 160 || message->FindString("name", &name) != B_OK) 161 break; 162 163 // TODO: make this better (possible under Haiku) 164 snooze(100000); 165 // let the font be written completely before trying to open it 166 167 BEntry entry; 168 if (set_entry(nodeRef, name, entry) != B_OK) 169 break; 170 171 if (entry.IsDirectory()) { 172 // a new directory to watch for us 173 _AddPath(entry); 174 } else { 175 // a new font 176 font_directory* directory = _FindDirectory(nodeRef); 177 if (directory == NULL) { 178 // unknown directory? how come? 179 break; 180 } 181 182 _AddFont(*directory, entry); 183 } 184 break; 185 } 186 187 case B_ENTRY_MOVED: 188 { 189 // has the entry been moved into a monitored directory or has 190 // it been removed from one? 191 const char* name; 192 node_ref nodeRef; 193 uint64 fromNode; 194 uint64 node; 195 if (message->FindInt32("device", &nodeRef.device) != B_OK 196 || message->FindInt64("to directory", &nodeRef.node) != B_OK 197 || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK 198 || message->FindInt64("node", (int64 *)&node) != B_OK 199 || message->FindString("name", &name) != B_OK) 200 break; 201 202 font_directory* directory = _FindDirectory(nodeRef); 203 204 BEntry entry; 205 if (set_entry(nodeRef, name, entry) != B_OK) 206 break; 207 208 if (directory != NULL) { 209 // something has been added to our watched font directories 210 211 // test, if the source directory is one of ours as well 212 nodeRef.node = fromNode; 213 font_directory* fromDirectory = _FindDirectory(nodeRef); 214 215 if (entry.IsDirectory()) { 216 if (fromDirectory == NULL) { 217 // there is a new directory to watch for us 218 _AddPath(entry); 219 FTRACE(("new directory moved in")); 220 } else { 221 // A directory from our watched directories has 222 // been renamed or moved within the watched 223 // directories - we only need to update the 224 // path names of the styles in that directory 225 nodeRef.node = node; 226 directory = _FindDirectory(nodeRef); 227 if (directory != NULL) { 228 for (int32 i = 0; i < directory->styles.CountItems(); i++) { 229 FontStyle* style = directory->styles.ItemAt(i); 230 style->UpdatePath(directory->directory); 231 } 232 } 233 FTRACE(("directory renamed")); 234 } 235 } else { 236 if (fromDirectory != NULL) { 237 // find style in source and move it to the target 238 nodeRef.node = node; 239 FontStyle* style = fromDirectory->FindStyle(nodeRef); 240 if (style != NULL) { 241 fromDirectory->styles.RemoveItem(style, false); 242 directory->styles.AddItem(style); 243 style->UpdatePath(directory->directory); 244 } 245 FTRACE(("font moved")); 246 } else { 247 FTRACE(("font added: %s\n", name)); 248 _AddFont(*directory, entry); 249 } 250 } 251 } else { 252 // and entry has been removed from our font directories 253 if (entry.IsDirectory()) { 254 if (entry.GetNodeRef(&nodeRef) == B_OK 255 && (directory = _FindDirectory(nodeRef)) != NULL) 256 _RemoveDirectory(directory); 257 } else { 258 // remove font style from directory 259 _RemoveStyle(nodeRef.device, fromNode, node); 260 } 261 } 262 break; 263 } 264 265 case B_ENTRY_REMOVED: 266 { 267 node_ref nodeRef; 268 uint64 directoryNode; 269 if (message->FindInt32("device", &nodeRef.device) != B_OK 270 || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK 271 || message->FindInt64("node", &nodeRef.node) != B_OK) 272 break; 273 274 font_directory* directory = _FindDirectory(nodeRef); 275 if (directory != NULL) { 276 // the directory has been removed, so we remove it as well 277 _RemoveDirectory(directory); 278 } else { 279 // remove font style from directory 280 _RemoveStyle(nodeRef.device, directoryNode, nodeRef.node); 281 } 282 break; 283 } 284 } 285 break; 286 } 287 288 default: 289 FontManagerBase::MessageReceived(message); 290 break; 291 } 292 293 // Scan fonts here if we need to, preventing other threads from having to do so. 294 _ScanFontsIfNecessary(); 295 } 296 297 298 int32 299 GlobalFontManager::CheckRevision(uid_t user) 300 { 301 BAutolock locker(this); 302 int32 revision = 0; 303 304 _ScanFontsIfNecessary(); 305 306 for (int32 i = 0; i < fDirectories.CountItems(); i++) { 307 font_directory* directory = fDirectories.ItemAt(i); 308 309 // TODO: for now, add all directories 310 revision += directory->revision; 311 } 312 313 return revision; 314 } 315 316 317 void 318 GlobalFontManager::SaveRecentFontMappings() 319 { 320 } 321 322 323 void 324 GlobalFontManager::_AddDefaultMapping(const char* family, const char* style, 325 const char* path) 326 { 327 font_mapping* mapping = new (std::nothrow) font_mapping; 328 if (mapping == NULL) 329 return; 330 331 mapping->family = family; 332 mapping->style = style; 333 BEntry entry(path); 334 335 if (entry.GetRef(&mapping->ref) != B_OK 336 || !entry.Exists() 337 || !fMappings.AddItem(mapping)) 338 delete mapping; 339 } 340 341 342 bool 343 GlobalFontManager::_LoadRecentFontMappings() 344 { 345 // default known mappings 346 // TODO: load them for real, and use these as a fallback 347 348 BPath ttfontsPath; 349 if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) { 350 ttfontsPath.Append("ttfonts"); 351 352 BPath veraFontPath = ttfontsPath; 353 veraFontPath.Append("NotoSansDisplay-Regular.ttf"); 354 _AddDefaultMapping("Noto Sans Display", "Book", veraFontPath.Path()); 355 356 veraFontPath.SetTo(ttfontsPath.Path()); 357 veraFontPath.Append("NotoSansDisplay-Bold.ttf"); 358 _AddDefaultMapping("Noto Sans Display", "Bold", veraFontPath.Path()); 359 360 veraFontPath.SetTo(ttfontsPath.Path()); 361 veraFontPath.Append("NotoSansMono-Regular.ttf"); 362 _AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path()); 363 364 return true; 365 } 366 367 return false; 368 } 369 370 371 status_t 372 GlobalFontManager::_AddMappedFont(const char* familyName, const char* styleName) 373 { 374 FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n", 375 familyName, styleName)); 376 377 for (int32 i = 0; i < fMappings.CountItems(); i++) { 378 font_mapping* mapping = fMappings.ItemAt(i); 379 380 if (mapping->family == familyName) { 381 if (styleName != NULL && mapping->style != styleName) 382 continue; 383 384 BEntry entry(&mapping->ref); 385 if (entry.InitCheck() != B_OK) 386 continue; 387 388 // find parent directory 389 390 node_ref nodeRef; 391 nodeRef.device = mapping->ref.device; 392 nodeRef.node = mapping->ref.directory; 393 font_directory* directory = _FindDirectory(nodeRef); 394 if (directory == NULL) { 395 // unknown directory, maybe this is a user font - try 396 // to create the missing directory 397 BPath path(&entry); 398 if (path.GetParent(&path) != B_OK 399 || _CreateDirectories(path.Path()) != B_OK 400 || (directory = _FindDirectory(nodeRef)) == NULL) 401 continue; 402 } 403 404 return _AddFont(*directory, entry); 405 } 406 } 407 408 return B_ENTRY_NOT_FOUND; 409 } 410 411 412 FontStyle* 413 GlobalFontManager::_GetDefaultStyle(const char* familyName, const char* styleName, 414 const char* fallbackFamily, const char* fallbackStyle, 415 uint16 fallbackFace) 416 { 417 // try to find a matching font 418 FontStyle* style = GetStyle(familyName, styleName); 419 if (style == NULL) { 420 style = GetStyle(fallbackFamily, fallbackStyle); 421 if (style == NULL) { 422 style = FindStyleMatchingFace(fallbackFace); 423 if (style == NULL && FamilyAt(0) != NULL) 424 style = FamilyAt(0)->StyleAt(0); 425 } 426 } 427 428 return style; 429 } 430 431 432 /*! \brief Sets the fonts that will be used when you create an empty 433 ServerFont without specifying a style, as well as the default 434 Desktop fonts if there are no settings available. 435 */ 436 status_t 437 GlobalFontManager::_SetDefaultFonts() 438 { 439 FontStyle* style = NULL; 440 441 // plain font 442 style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, DEFAULT_PLAIN_FONT_STYLE, 443 FALLBACK_PLAIN_FONT_FAMILY, FALLBACK_PLAIN_FONT_STYLE, B_REGULAR_FACE); 444 if (style == NULL) 445 return B_ERROR; 446 447 fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style, 448 DEFAULT_FONT_SIZE)); 449 if (!fDefaultPlainFont.IsSet()) 450 return B_NO_MEMORY; 451 452 // bold font 453 style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, 454 FALLBACK_BOLD_FONT_FAMILY, FALLBACK_BOLD_FONT_STYLE, B_BOLD_FACE); 455 456 fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style, 457 DEFAULT_FONT_SIZE)); 458 if (!fDefaultBoldFont.IsSet()) 459 return B_NO_MEMORY; 460 461 // fixed font 462 style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, 463 FALLBACK_FIXED_FONT_FAMILY, FALLBACK_FIXED_FONT_STYLE, B_REGULAR_FACE); 464 465 fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style, 466 DEFAULT_FONT_SIZE)); 467 if (!fDefaultFixedFont.IsSet()) 468 return B_NO_MEMORY; 469 470 fDefaultFixedFont->SetSpacing(B_FIXED_SPACING); 471 472 return B_OK; 473 } 474 475 476 /*! \brief Removes the style from the font directory. 477 478 It doesn't necessary delete the font style, if it's still 479 in use, though. 480 */ 481 void 482 GlobalFontManager::_RemoveStyle(font_directory& directory, FontStyle* style) 483 { 484 FTRACE(("font removed: %s\n", style->Name())); 485 486 directory.styles.RemoveItem(style); 487 directory.revision++; 488 489 fStyleHashTable.Remove(FontKey(style->Family()->ID(), style->ID())); 490 } 491 492 493 void 494 GlobalFontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node) 495 { 496 // remove font style from directory 497 node_ref nodeRef; 498 nodeRef.device = device; 499 nodeRef.node = directoryNode; 500 501 font_directory* directory = _FindDirectory(nodeRef); 502 if (directory != NULL) { 503 // find style in directory and remove it 504 nodeRef.node = node; 505 FontStyle* style = directory->FindStyle(nodeRef); 506 if (style != NULL) 507 _RemoveStyle(*directory, style); 508 } 509 } 510 511 512 /*! \brief Counts the number of font families available 513 \return The number of unique font families currently available 514 */ 515 int32 516 GlobalFontManager::CountFamilies() 517 { 518 _ScanFontsIfNecessary(); 519 520 return fFamilies.CountItems(); 521 } 522 523 524 /*! \brief Counts the number of styles available in a font family 525 \param family Name of the font family to scan 526 \return The number of font styles currently available for the font family 527 */ 528 int32 529 GlobalFontManager::CountStyles(const char* familyName) 530 { 531 _ScanFontsIfNecessary(); 532 533 FontFamily* family = GetFamily(familyName); 534 if (family) 535 return family->CountStyles(); 536 537 return 0; 538 } 539 540 541 /*! \brief Counts the number of styles available in a font family 542 \param family Name of the font family to scan 543 \return The number of font styles currently available for the font family 544 */ 545 int32 546 GlobalFontManager::CountStyles(uint16 familyID) 547 { 548 _ScanFontsIfNecessary(); 549 550 FontFamily* family = GetFamily(familyID); 551 if (family) 552 return family->CountStyles(); 553 554 return 0; 555 } 556 557 558 FontStyle* 559 GlobalFontManager::GetStyle(uint16 familyID, uint16 styleID) const 560 { 561 return FontManagerBase::GetStyle(familyID, styleID); 562 } 563 564 565 /*! \brief Retrieves the FontStyle object that comes closest to the one 566 specified. 567 568 \param family The font's family or NULL in which case \a familyID is used 569 \param style The font's style or NULL in which case \a styleID is used 570 \param familyID will only be used if \a family is NULL (or empty) 571 \param styleID will only be used if \a style is NULL (or empty) 572 \param face is used to specify the style if both \a style is NULL or empty 573 and styleID is 0xffff. 574 575 \return The FontStyle having those attributes or NULL if not available 576 */ 577 FontStyle* 578 GlobalFontManager::GetStyle(const char* familyName, const char* styleName, 579 uint16 familyID, uint16 styleID, uint16 face) 580 { 581 ASSERT(IsLocked()); 582 583 FontFamily* family; 584 585 // find family 586 587 if (familyName != NULL && familyName[0]) 588 family = GetFamily(familyName); 589 else 590 family = GetFamily(familyID); 591 592 if (family == NULL) 593 return NULL; 594 595 // find style 596 597 if (styleName != NULL && styleName[0]) { 598 FontStyle* fontStyle = family->GetStyle(styleName); 599 if (fontStyle != NULL) 600 return fontStyle; 601 602 // before we fail, we try the mappings for a match 603 if (_AddMappedFont(family->Name(), styleName) == B_OK) { 604 fontStyle = family->GetStyle(styleName); 605 if (fontStyle != NULL) 606 return fontStyle; 607 } 608 609 _ScanFonts(); 610 return family->GetStyle(styleName); 611 } 612 613 if (styleID != 0xffff) 614 return family->GetStyleByID(styleID); 615 616 // try to get from face 617 return family->GetStyleMatchingFace(face); 618 } 619 620 621 void 622 GlobalFontManager::_PrecacheFontFile(const ServerFont* font) 623 { 624 if (font == NULL) 625 return; 626 627 size_t bufferSize = 32768; 628 uint8* buffer = new (std::nothrow) uint8[bufferSize]; 629 if (buffer == NULL) { 630 // We don't care. Pre-caching doesn't make sense anyways when there 631 // is not enough RAM... 632 return; 633 } 634 635 BFile file(font->Path(), B_READ_ONLY); 636 if (file.InitCheck() != B_OK) { 637 delete[] buffer; 638 return; 639 } 640 641 while (true) { 642 // We just want the file in the kernel file cache... 643 ssize_t read = file.Read(buffer, bufferSize); 644 if (read < (ssize_t)bufferSize) 645 break; 646 } 647 648 delete[] buffer; 649 } 650 651 652 void 653 GlobalFontManager::_AddSystemPaths() 654 { 655 BPath path; 656 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) 657 _AddPath(path.Path()); 658 659 // We don't scan these in test mode to help shave off some startup time 660 #if !TEST_MODE 661 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) 662 _AddPath(path.Path()); 663 #endif 664 } 665 666 667 void 668 GlobalFontManager::_ScanFontsIfNecessary() 669 { 670 if (!fScanned) 671 _ScanFonts(); 672 } 673 674 675 //! Scans all currently known font directories 676 void 677 GlobalFontManager::_ScanFonts() 678 { 679 if (fScanned) 680 return; 681 682 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 683 font_directory* directory = fDirectories.ItemAt(i); 684 685 if (directory->AlreadyScanned()) 686 continue; 687 688 _ScanFontDirectory(*directory); 689 } 690 691 fScanned = true; 692 } 693 694 695 /*! \brief Adds the FontFamily/FontStyle that is represented by this path. 696 */ 697 status_t 698 GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry) 699 { 700 node_ref nodeRef; 701 status_t status = entry.GetNodeRef(&nodeRef); 702 if (status < B_OK) 703 return status; 704 705 BPath path; 706 status = entry.GetPath(&path); 707 if (status < B_OK) 708 return status; 709 710 FT_Face face; 711 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face); 712 if (error != 0) 713 return B_ERROR; 714 715 FontFamily* family = _FindFamily(face->family_name); 716 if (family != NULL && family->HasStyle(face->style_name)) { 717 // prevent adding the same style twice 718 // (this indicates a problem with the installed fonts maybe?) 719 FT_Done_Face(face); 720 return B_OK; 721 } 722 723 if (family == NULL) { 724 family = new (std::nothrow) FontFamily(face->family_name, fNextID++); 725 if (family == NULL 726 || !fFamilies.BinaryInsert(family, compare_font_families)) { 727 delete family; 728 FT_Done_Face(face); 729 return B_NO_MEMORY; 730 } 731 } 732 733 FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name)); 734 735 // the FontStyle takes over ownership of the FT_Face object 736 FontStyle* style = new (std::nothrow) FontStyle(nodeRef, path.Path(), face, this); 737 if (style == NULL || !family->AddStyle(style)) { 738 delete style; 739 delete family; 740 return B_NO_MEMORY; 741 } 742 743 directory.styles.AddItem(style); 744 fStyleHashTable.Put(FontKey(style->Family()->ID(), style->ID()), style); 745 style->ReleaseReference(); 746 747 if (directory.AlreadyScanned()) 748 directory.revision++; 749 750 return B_OK; 751 } 752 753 754 GlobalFontManager::font_directory* 755 GlobalFontManager::_FindDirectory(node_ref& nodeRef) 756 { 757 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 758 font_directory* directory = fDirectories.ItemAt(i); 759 760 if (directory->directory == nodeRef) 761 return directory; 762 } 763 764 return NULL; 765 } 766 767 768 void 769 GlobalFontManager::_RemoveDirectory(font_directory* directory) 770 { 771 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n", 772 directory->directory.node)); 773 774 fDirectories.RemoveItem(directory, false); 775 776 // TODO: remove styles from this directory! 777 778 watch_node(&directory->directory, B_STOP_WATCHING, this); 779 delete directory; 780 } 781 782 783 status_t 784 GlobalFontManager::_AddPath(const char* path) 785 { 786 BEntry entry; 787 status_t status = entry.SetTo(path); 788 if (status != B_OK) 789 return status; 790 791 return _AddPath(entry); 792 } 793 794 795 status_t 796 GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory) 797 { 798 node_ref nodeRef; 799 status_t status = entry.GetNodeRef(&nodeRef); 800 if (status != B_OK) 801 return status; 802 803 // check if we are already know this directory 804 805 font_directory* directory = _FindDirectory(nodeRef); 806 if (directory != NULL) { 807 if (_newDirectory) 808 *_newDirectory = directory; 809 return B_OK; 810 } 811 812 // it's a new one, so let's add it 813 814 directory = new (std::nothrow) font_directory; 815 if (directory == NULL) 816 return B_NO_MEMORY; 817 818 struct stat stat; 819 status = entry.GetStat(&stat); 820 if (status != B_OK) { 821 delete directory; 822 return status; 823 } 824 825 directory->directory = nodeRef; 826 directory->user = stat.st_uid; 827 directory->group = stat.st_gid; 828 directory->revision = 0; 829 830 status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this); 831 if (status != B_OK) { 832 // we cannot watch this directory - while this is unfortunate, 833 // it's not a critical error 834 printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n", 835 nodeRef.device, nodeRef.node); 836 // TODO: should go into syslog() 837 } else { 838 BPath path(&entry); 839 FTRACE(("FontManager: now watching: %s\n", path.Path())); 840 } 841 842 fDirectories.AddItem(directory); 843 844 if (_newDirectory) 845 *_newDirectory = directory; 846 847 fScanned = false; 848 return B_OK; 849 } 850 851 852 /*! \brief Creates all unknown font_directories of the specified path - but 853 only if one of its parent directories is already known. 854 855 This method is used to create the font_directories for font_mappings. 856 It recursively walks upwards in the directory hierarchy until it finds 857 a known font_directory (or hits the root directory, in which case it 858 bails out). 859 */ 860 status_t 861 GlobalFontManager::_CreateDirectories(const char* path) 862 { 863 FTRACE(("_CreateDirectories(path = %s)\n", path)); 864 865 if (!strcmp(path, "/")) { 866 // we walked our way up to the root 867 return B_ENTRY_NOT_FOUND; 868 } 869 870 BEntry entry; 871 status_t status = entry.SetTo(path); 872 if (status != B_OK) 873 return status; 874 875 node_ref nodeRef; 876 status = entry.GetNodeRef(&nodeRef); 877 if (status != B_OK) 878 return status; 879 880 // check if we are already know this directory 881 882 font_directory* directory = _FindDirectory(nodeRef); 883 if (directory != NULL) 884 return B_OK; 885 886 // We don't know this one yet - keep walking the path upwards 887 // and try to find a match. 888 889 BPath parent(path); 890 status = parent.GetParent(&parent); 891 if (status != B_OK) 892 return status; 893 894 status = _CreateDirectories(parent.Path()); 895 if (status != B_OK) 896 return status; 897 898 // We have our match, create sub directory 899 900 return _AddPath(path); 901 } 902 903 904 /*! \brief Scan a folder for all valid fonts 905 \param directoryPath Path of the folder to scan. 906 */ 907 status_t 908 GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory) 909 { 910 // This bad boy does all the real work. It loads each entry in the 911 // directory. If a valid font file, it adds both the family and the style. 912 913 BDirectory directory; 914 status_t status = directory.SetTo(&fontDirectory.directory); 915 if (status != B_OK) 916 return status; 917 918 BEntry entry; 919 while (directory.GetNextEntry(&entry) == B_OK) { 920 if (entry.IsDirectory()) { 921 // scan this directory recursively 922 font_directory* newDirectory; 923 if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL) 924 _ScanFontDirectory(*newDirectory); 925 926 continue; 927 } 928 929 // TODO: Commenting this out makes my "Unicode glyph lookup" 930 // work with our default fonts. The real fix is to select the 931 // Unicode char map (if supported), and/or adjust the 932 // utf8 -> glyph-index mapping everywhere to handle other 933 // char maps. We could also ignore fonts that don't support 934 // the Unicode lookup as a temporary "solution". 935 #if 0 936 FT_CharMap charmap = _GetSupportedCharmap(face); 937 if (!charmap) { 938 FT_Done_Face(face); 939 continue; 940 } 941 942 face->charmap = charmap; 943 #endif 944 945 _AddFont(fontDirectory, entry); 946 // takes over ownership of the FT_Face object 947 } 948 949 fontDirectory.revision = 1; 950 return B_OK; 951 } 952 953 954 /*! \brief Locates a FontFamily object by name 955 \param name The family to find 956 \return Pointer to the specified family or NULL if not found. 957 */ 958 FontFamily* 959 GlobalFontManager::GetFamily(const char* name) 960 { 961 if (name == NULL) 962 return NULL; 963 964 FontFamily* family = _FindFamily(name); 965 if (family != NULL) 966 return family; 967 968 if (fScanned) 969 return NULL; 970 971 // try font mappings before failing 972 if (_AddMappedFont(name) == B_OK) 973 return _FindFamily(name); 974 975 _ScanFonts(); 976 return _FindFamily(name); 977 } 978 979 980 FontFamily* 981 GlobalFontManager::GetFamily(uint16 familyID) const 982 { 983 FontKey key(familyID, 0); 984 FontStyle* style = fStyleHashTable.Get(key); 985 if (style != NULL) 986 return style->Family(); 987 988 return NULL; 989 } 990 991 992 const ServerFont* 993 GlobalFontManager::DefaultPlainFont() const 994 { 995 return fDefaultPlainFont.Get(); 996 } 997 998 999 const ServerFont* 1000 GlobalFontManager::DefaultBoldFont() const 1001 { 1002 return fDefaultBoldFont.Get(); 1003 } 1004 1005 1006 const ServerFont* 1007 GlobalFontManager::DefaultFixedFont() const 1008 { 1009 return fDefaultFixedFont.Get(); 1010 } 1011 1012 1013 void 1014 GlobalFontManager::AttachUser(uid_t userID) 1015 { 1016 BAutolock locker(this); 1017 1018 #if !TEST_MODE 1019 // TODO: actually, find_directory() cannot know which user ID we want here 1020 // TODO: avoids user fonts in safe mode 1021 BPath path; 1022 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) 1023 _AddPath(path.Path()); 1024 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) 1025 == B_OK) { 1026 _AddPath(path.Path()); 1027 } 1028 #endif 1029 } 1030 1031 1032 void 1033 GlobalFontManager::DetachUser(uid_t userID) 1034 { 1035 BAutolock locker(this); 1036 1037 // TODO! 1038 } 1039