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 "FontFamily.h" 30 #include "FontManager.h" 31 #include "ServerConfig.h" 32 #include "ServerFont.h" 33 34 35 //#define TRACE_FONT_MANAGER 36 #ifdef TRACE_GLOBAL_FONT_MANAGER 37 # define FTRACE(x) printf x 38 #else 39 # define FTRACE(x) ; 40 #endif 41 42 43 // TODO: needs some more work for multi-user support 44 45 GlobalFontManager* gFontManager = NULL; 46 47 48 struct GlobalFontManager::font_directory { 49 node_ref directory; 50 uid_t user; 51 gid_t group; 52 uint32 revision; 53 BObjectList<FontStyle> styles; 54 55 bool AlreadyScanned() const { return revision != 0; } 56 FontStyle* FindStyle(const node_ref& nodeRef) const; 57 }; 58 59 60 struct GlobalFontManager::font_mapping { 61 BString family; 62 BString style; 63 entry_ref ref; 64 }; 65 66 67 FontStyle* 68 GlobalFontManager::font_directory::FindStyle(const node_ref& nodeRef) const 69 { 70 for (int32 i = styles.CountItems(); i-- > 0;) { 71 FontStyle* style = styles.ItemAt(i); 72 73 if (nodeRef == style->NodeRef()) 74 return style; 75 } 76 77 return NULL; 78 } 79 80 81 static status_t 82 set_entry(node_ref& nodeRef, const char* name, BEntry& entry) 83 { 84 entry_ref ref; 85 ref.device = nodeRef.device; 86 ref.directory = nodeRef.node; 87 88 status_t status = ref.set_name(name); 89 if (status != B_OK) 90 return status; 91 92 return entry.SetTo(&ref); 93 } 94 95 96 // #pragma mark - 97 98 99 //! Does basic set up so that directories can be scanned 100 GlobalFontManager::GlobalFontManager() 101 : FontManagerBase(true, "GlobalFontManager"), 102 fDirectories(10, true), 103 fMappings(10, true), 104 105 fDefaultPlainFont(NULL), 106 fDefaultBoldFont(NULL), 107 fDefaultFixedFont(NULL), 108 109 fScanned(false) 110 { 111 if (InitCheck() == B_OK) { 112 _AddSystemPaths(); 113 _LoadRecentFontMappings(); 114 115 status_t status = _SetDefaultFonts(); 116 117 if (status == B_OK) { 118 // Precache the plain and bold fonts 119 _PrecacheFontFile(fDefaultPlainFont.Get()); 120 _PrecacheFontFile(fDefaultBoldFont.Get()); 121 122 // Post a message so we scan the initial paths. 123 PostMessage(B_PULSE); 124 } 125 126 SetInitStatus(status); 127 } 128 } 129 130 131 //! Frees items allocated in the constructor and deletes all global families 132 GlobalFontManager::~GlobalFontManager() 133 { 134 fDefaultPlainFont.Unset(); 135 fDefaultBoldFont.Unset(); 136 fDefaultFixedFont.Unset(); 137 138 while (fFamilies.CountItems() > 0) 139 delete fFamilies.ItemAt(0); 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 // plain font 440 FontStyle* style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, 441 DEFAULT_PLAIN_FONT_STYLE, FALLBACK_PLAIN_FONT_FAMILY, 442 DEFAULT_PLAIN_FONT_STYLE, 443 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, DEFAULT_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, DEFAULT_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 style->ReleaseReference(); 492 } 493 494 495 void 496 GlobalFontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node) 497 { 498 // remove font style from directory 499 node_ref nodeRef; 500 nodeRef.device = device; 501 nodeRef.node = directoryNode; 502 503 font_directory* directory = _FindDirectory(nodeRef); 504 if (directory != NULL) { 505 // find style in directory and remove it 506 nodeRef.node = node; 507 FontStyle* style = directory->FindStyle(nodeRef); 508 if (style != NULL) 509 _RemoveStyle(*directory, style); 510 } 511 } 512 513 514 /*! \brief Counts the number of font families available 515 \return The number of unique font families currently available 516 */ 517 int32 518 GlobalFontManager::CountFamilies() 519 { 520 _ScanFontsIfNecessary(); 521 522 return fFamilies.CountItems(); 523 } 524 525 526 /*! \brief Counts the number of styles available in a font family 527 \param family Name of the font family to scan 528 \return The number of font styles currently available for the font family 529 */ 530 int32 531 GlobalFontManager::CountStyles(const char* familyName) 532 { 533 _ScanFontsIfNecessary(); 534 535 FontFamily* family = GetFamily(familyName); 536 if (family) 537 return family->CountStyles(); 538 539 return 0; 540 } 541 542 543 /*! \brief Counts the number of styles available in a font family 544 \param family Name of the font family to scan 545 \return The number of font styles currently available for the font family 546 */ 547 int32 548 GlobalFontManager::CountStyles(uint16 familyID) 549 { 550 _ScanFontsIfNecessary(); 551 552 FontFamily* family = GetFamily(familyID); 553 if (family) 554 return family->CountStyles(); 555 556 return 0; 557 } 558 559 560 FontStyle* 561 GlobalFontManager::GetStyle(uint16 familyID, uint16 styleID) const 562 { 563 return FontManagerBase::GetStyle(familyID, styleID); 564 } 565 566 567 /*! \brief Retrieves the FontStyle object that comes closest to the one 568 specified. 569 570 \param family The font's family or NULL in which case \a familyID is used 571 \param style The font's style or NULL in which case \a styleID is used 572 \param familyID will only be used if \a family is NULL (or empty) 573 \param styleID will only be used if \a style is NULL (or empty) 574 \param face is used to specify the style if both \a style is NULL or empty 575 and styleID is 0xffff. 576 577 \return The FontStyle having those attributes or NULL if not available 578 */ 579 FontStyle* 580 GlobalFontManager::GetStyle(const char* familyName, const char* styleName, 581 uint16 familyID, uint16 styleID, uint16 face) 582 { 583 ASSERT(IsLocked()); 584 585 FontFamily* family; 586 587 // find family 588 589 if (familyName != NULL && familyName[0]) 590 family = GetFamily(familyName); 591 else 592 family = GetFamily(familyID); 593 594 if (family == NULL) 595 return NULL; 596 597 // find style 598 599 if (styleName != NULL && styleName[0]) { 600 FontStyle* fontStyle = family->GetStyle(styleName); 601 if (fontStyle != NULL) 602 return fontStyle; 603 604 // before we fail, we try the mappings for a match 605 if (_AddMappedFont(family->Name(), styleName) == B_OK) { 606 fontStyle = family->GetStyle(styleName); 607 if (fontStyle != NULL) 608 return fontStyle; 609 } 610 611 _ScanFonts(); 612 return family->GetStyle(styleName); 613 } 614 615 if (styleID != 0xffff) 616 return family->GetStyleByID(styleID); 617 618 // try to get from face 619 return family->GetStyleMatchingFace(face); 620 } 621 622 623 void 624 GlobalFontManager::_PrecacheFontFile(const ServerFont* font) 625 { 626 if (font == NULL) 627 return; 628 629 size_t bufferSize = 32768; 630 uint8* buffer = new (std::nothrow) uint8[bufferSize]; 631 if (buffer == NULL) { 632 // We don't care. Pre-caching doesn't make sense anyways when there 633 // is not enough RAM... 634 return; 635 } 636 637 BFile file(font->Path(), B_READ_ONLY); 638 if (file.InitCheck() != B_OK) { 639 delete[] buffer; 640 return; 641 } 642 643 while (true) { 644 // We just want the file in the kernel file cache... 645 ssize_t read = file.Read(buffer, bufferSize); 646 if (read < (ssize_t)bufferSize) 647 break; 648 } 649 650 delete[] buffer; 651 } 652 653 654 void 655 GlobalFontManager::_AddSystemPaths() 656 { 657 BPath path; 658 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) 659 _AddPath(path.Path()); 660 661 // We don't scan these in test mode to help shave off some startup time 662 #if !TEST_MODE 663 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) 664 _AddPath(path.Path()); 665 #endif 666 } 667 668 669 void 670 GlobalFontManager::_ScanFontsIfNecessary() 671 { 672 if (!fScanned) 673 _ScanFonts(); 674 } 675 676 677 //! Scans all currently known font directories 678 void 679 GlobalFontManager::_ScanFonts() 680 { 681 if (fScanned) 682 return; 683 684 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 685 font_directory* directory = fDirectories.ItemAt(i); 686 687 if (directory->AlreadyScanned()) 688 continue; 689 690 _ScanFontDirectory(*directory); 691 } 692 693 fScanned = true; 694 } 695 696 697 /*! \brief Adds the FontFamily/FontStyle that is represented by this path. 698 */ 699 status_t 700 GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry) 701 { 702 node_ref nodeRef; 703 status_t status = entry.GetNodeRef(&nodeRef); 704 if (status < B_OK) 705 return status; 706 707 BPath path; 708 status = entry.GetPath(&path); 709 if (status < B_OK) 710 return status; 711 712 FT_Face face; 713 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face); 714 if (error != 0) 715 return B_ERROR; 716 717 FontFamily* family = _FindFamily(face->family_name); 718 if (family != NULL && family->HasStyle(face->style_name)) { 719 // prevent adding the same style twice 720 // (this indicates a problem with the installed fonts maybe?) 721 FT_Done_Face(face); 722 return B_OK; 723 } 724 725 if (family == NULL) { 726 family = new (std::nothrow) FontFamily(face->family_name, fNextID++); 727 if (family == NULL 728 || !fFamilies.BinaryInsert(family, compare_font_families)) { 729 delete family; 730 FT_Done_Face(face); 731 return B_NO_MEMORY; 732 } 733 } 734 735 FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name)); 736 737 // the FontStyle takes over ownership of the FT_Face object 738 FontStyle* style = new (std::nothrow) FontStyle(nodeRef, path.Path(), face); 739 if (style == NULL || !family->AddStyle(style)) { 740 delete style; 741 delete family; 742 return B_NO_MEMORY; 743 } 744 745 directory.styles.AddItem(style); 746 fStyleHashTable.Put(FontKey(style->Family()->ID(), style->ID()), style); 747 748 if (directory.AlreadyScanned()) 749 directory.revision++; 750 751 return B_OK; 752 } 753 754 755 GlobalFontManager::font_directory* 756 GlobalFontManager::_FindDirectory(node_ref& nodeRef) 757 { 758 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 759 font_directory* directory = fDirectories.ItemAt(i); 760 761 if (directory->directory == nodeRef) 762 return directory; 763 } 764 765 return NULL; 766 } 767 768 769 void 770 GlobalFontManager::_RemoveDirectory(font_directory* directory) 771 { 772 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n", 773 directory->directory.node)); 774 775 fDirectories.RemoveItem(directory, false); 776 777 // TODO: remove styles from this directory! 778 779 watch_node(&directory->directory, B_STOP_WATCHING, this); 780 delete directory; 781 } 782 783 784 status_t 785 GlobalFontManager::_AddPath(const char* path) 786 { 787 BEntry entry; 788 status_t status = entry.SetTo(path); 789 if (status != B_OK) 790 return status; 791 792 return _AddPath(entry); 793 } 794 795 796 status_t 797 GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory) 798 { 799 node_ref nodeRef; 800 status_t status = entry.GetNodeRef(&nodeRef); 801 if (status != B_OK) 802 return status; 803 804 // check if we are already know this directory 805 806 font_directory* directory = _FindDirectory(nodeRef); 807 if (directory != NULL) { 808 if (_newDirectory) 809 *_newDirectory = directory; 810 return B_OK; 811 } 812 813 // it's a new one, so let's add it 814 815 directory = new (std::nothrow) font_directory; 816 if (directory == NULL) 817 return B_NO_MEMORY; 818 819 struct stat stat; 820 status = entry.GetStat(&stat); 821 if (status != B_OK) { 822 delete directory; 823 return status; 824 } 825 826 directory->directory = nodeRef; 827 directory->user = stat.st_uid; 828 directory->group = stat.st_gid; 829 directory->revision = 0; 830 831 status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this); 832 if (status != B_OK) { 833 // we cannot watch this directory - while this is unfortunate, 834 // it's not a critical error 835 printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n", 836 nodeRef.device, nodeRef.node); 837 // TODO: should go into syslog() 838 } else { 839 BPath path(&entry); 840 FTRACE(("FontManager: now watching: %s\n", path.Path())); 841 } 842 843 fDirectories.AddItem(directory); 844 845 if (_newDirectory) 846 *_newDirectory = directory; 847 848 fScanned = false; 849 return B_OK; 850 } 851 852 853 /*! \brief Creates all unknown font_directories of the specified path - but 854 only if one of its parent directories is already known. 855 856 This method is used to create the font_directories for font_mappings. 857 It recursively walks upwards in the directory hierarchy until it finds 858 a known font_directory (or hits the root directory, in which case it 859 bails out). 860 */ 861 status_t 862 GlobalFontManager::_CreateDirectories(const char* path) 863 { 864 FTRACE(("_CreateDirectories(path = %s)\n", path)); 865 866 if (!strcmp(path, "/")) { 867 // we walked our way up to the root 868 return B_ENTRY_NOT_FOUND; 869 } 870 871 BEntry entry; 872 status_t status = entry.SetTo(path); 873 if (status != B_OK) 874 return status; 875 876 node_ref nodeRef; 877 status = entry.GetNodeRef(&nodeRef); 878 if (status != B_OK) 879 return status; 880 881 // check if we are already know this directory 882 883 font_directory* directory = _FindDirectory(nodeRef); 884 if (directory != NULL) 885 return B_OK; 886 887 // We don't know this one yet - keep walking the path upwards 888 // and try to find a match. 889 890 BPath parent(path); 891 status = parent.GetParent(&parent); 892 if (status != B_OK) 893 return status; 894 895 status = _CreateDirectories(parent.Path()); 896 if (status != B_OK) 897 return status; 898 899 // We have our match, create sub directory 900 901 return _AddPath(path); 902 } 903 904 905 /*! \brief Scan a folder for all valid fonts 906 \param directoryPath Path of the folder to scan. 907 */ 908 status_t 909 GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory) 910 { 911 // This bad boy does all the real work. It loads each entry in the 912 // directory. If a valid font file, it adds both the family and the style. 913 914 BDirectory directory; 915 status_t status = directory.SetTo(&fontDirectory.directory); 916 if (status != B_OK) 917 return status; 918 919 BEntry entry; 920 while (directory.GetNextEntry(&entry) == B_OK) { 921 if (entry.IsDirectory()) { 922 // scan this directory recursively 923 font_directory* newDirectory; 924 if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL) 925 _ScanFontDirectory(*newDirectory); 926 927 continue; 928 } 929 930 // TODO: Commenting this out makes my "Unicode glyph lookup" 931 // work with our default fonts. The real fix is to select the 932 // Unicode char map (if supported), and/or adjust the 933 // utf8 -> glyph-index mapping everywhere to handle other 934 // char maps. We could also ignore fonts that don't support 935 // the Unicode lookup as a temporary "solution". 936 #if 0 937 FT_CharMap charmap = _GetSupportedCharmap(face); 938 if (!charmap) { 939 FT_Done_Face(face); 940 continue; 941 } 942 943 face->charmap = charmap; 944 #endif 945 946 _AddFont(fontDirectory, entry); 947 // takes over ownership of the FT_Face object 948 } 949 950 fontDirectory.revision = 1; 951 return B_OK; 952 } 953 954 955 /*! \brief Locates a FontFamily object by name 956 \param name The family to find 957 \return Pointer to the specified family or NULL if not found. 958 */ 959 FontFamily* 960 GlobalFontManager::GetFamily(const char* name) 961 { 962 if (name == NULL) 963 return NULL; 964 965 FontFamily* family = _FindFamily(name); 966 if (family != NULL) 967 return family; 968 969 if (fScanned) 970 return NULL; 971 972 // try font mappings before failing 973 if (_AddMappedFont(name) == B_OK) 974 return _FindFamily(name); 975 976 _ScanFonts(); 977 return _FindFamily(name); 978 } 979 980 981 FontFamily* 982 GlobalFontManager::GetFamily(uint16 familyID) const 983 { 984 FontKey key(familyID, 0); 985 FontStyle* style = fStyleHashTable.Get(key); 986 if (style != NULL) 987 return style->Family(); 988 989 return NULL; 990 } 991 992 993 const ServerFont* 994 GlobalFontManager::DefaultPlainFont() const 995 { 996 return fDefaultPlainFont.Get(); 997 } 998 999 1000 const ServerFont* 1001 GlobalFontManager::DefaultBoldFont() const 1002 { 1003 return fDefaultBoldFont.Get(); 1004 } 1005 1006 1007 const ServerFont* 1008 GlobalFontManager::DefaultFixedFont() const 1009 { 1010 return fDefaultFixedFont.Get(); 1011 } 1012 1013 1014 void 1015 GlobalFontManager::AttachUser(uid_t userID) 1016 { 1017 BAutolock locker(this); 1018 1019 #if !TEST_MODE 1020 // TODO: actually, find_directory() cannot know which user ID we want here 1021 // TODO: avoids user fonts in safe mode 1022 BPath path; 1023 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) 1024 _AddPath(path.Path()); 1025 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) 1026 == B_OK) { 1027 _AddPath(path.Path()); 1028 } 1029 #endif 1030 } 1031 1032 1033 void 1034 GlobalFontManager::DetachUser(uid_t userID) 1035 { 1036 BAutolock locker(this); 1037 1038 // TODO! 1039 } 1040