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