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 : BLooper("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 fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR; 115 if (fInitStatus == B_OK) { 116 _AddSystemPaths(); 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 int32 302 GlobalFontManager::CheckRevision(uid_t user) 303 { 304 BAutolock locker(this); 305 int32 revision = 0; 306 307 _ScanFontsIfNecessary(); 308 309 for (int32 i = 0; i < fDirectories.CountItems(); i++) { 310 font_directory* directory = fDirectories.ItemAt(i); 311 312 // TODO: for now, add all directories 313 revision += directory->revision; 314 } 315 316 return revision; 317 } 318 319 320 void 321 GlobalFontManager::SaveRecentFontMappings() 322 { 323 } 324 325 326 void 327 GlobalFontManager::_AddDefaultMapping(const char* family, const char* style, 328 const char* path) 329 { 330 font_mapping* mapping = new (std::nothrow) font_mapping; 331 if (mapping == NULL) 332 return; 333 334 mapping->family = family; 335 mapping->style = style; 336 BEntry entry(path); 337 338 if (entry.GetRef(&mapping->ref) != B_OK 339 || !entry.Exists() 340 || !fMappings.AddItem(mapping)) 341 delete mapping; 342 } 343 344 345 bool 346 GlobalFontManager::_LoadRecentFontMappings() 347 { 348 // default known mappings 349 // TODO: load them for real, and use these as a fallback 350 351 BPath ttfontsPath; 352 if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) { 353 ttfontsPath.Append("ttfonts"); 354 355 BPath veraFontPath = ttfontsPath; 356 veraFontPath.Append("NotoSans-Regular.ttf"); 357 _AddDefaultMapping("Noto Sans", "Book", veraFontPath.Path()); 358 359 veraFontPath.SetTo(ttfontsPath.Path()); 360 veraFontPath.Append("NotoSans-Bold.ttf"); 361 _AddDefaultMapping("Noto Sans", "Bold", veraFontPath.Path()); 362 363 veraFontPath.SetTo(ttfontsPath.Path()); 364 veraFontPath.Append("NotoSansMono-Regular.ttf"); 365 _AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path()); 366 367 return true; 368 } 369 370 return false; 371 } 372 373 374 status_t 375 GlobalFontManager::_AddMappedFont(const char* familyName, const char* styleName) 376 { 377 FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n", 378 familyName, styleName)); 379 380 for (int32 i = 0; i < fMappings.CountItems(); i++) { 381 font_mapping* mapping = fMappings.ItemAt(i); 382 383 if (mapping->family == familyName) { 384 if (styleName != NULL && mapping->style != styleName) 385 continue; 386 387 BEntry entry(&mapping->ref); 388 if (entry.InitCheck() != B_OK) 389 continue; 390 391 // find parent directory 392 393 node_ref nodeRef; 394 nodeRef.device = mapping->ref.device; 395 nodeRef.node = mapping->ref.directory; 396 font_directory* directory = _FindDirectory(nodeRef); 397 if (directory == NULL) { 398 // unknown directory, maybe this is a user font - try 399 // to create the missing directory 400 BPath path(&entry); 401 if (path.GetParent(&path) != B_OK 402 || _CreateDirectories(path.Path()) != B_OK 403 || (directory = _FindDirectory(nodeRef)) == NULL) 404 continue; 405 } 406 407 return _AddFont(*directory, entry); 408 } 409 } 410 411 return B_ENTRY_NOT_FOUND; 412 } 413 414 415 FontStyle* 416 GlobalFontManager::_GetDefaultStyle(const char* familyName, const char* styleName, 417 const char* fallbackFamily, const char* fallbackStyle, 418 uint16 fallbackFace) 419 { 420 // try to find a matching font 421 FontStyle* style = GetStyle(familyName, styleName); 422 if (style == NULL) { 423 style = GetStyle(fallbackFamily, fallbackStyle); 424 if (style == NULL) { 425 style = FindStyleMatchingFace(fallbackFace); 426 if (style == NULL && FamilyAt(0) != NULL) 427 style = FamilyAt(0)->StyleAt(0); 428 } 429 } 430 431 return style; 432 } 433 434 435 /*! \brief Sets the fonts that will be used when you create an empty 436 ServerFont without specifying a style, as well as the default 437 Desktop fonts if there are no settings available. 438 */ 439 status_t 440 GlobalFontManager::_SetDefaultFonts() 441 { 442 FontStyle* style = NULL; 443 444 // plain font 445 style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, DEFAULT_PLAIN_FONT_STYLE, 446 FALLBACK_PLAIN_FONT_FAMILY, FALLBACK_PLAIN_FONT_STYLE, B_REGULAR_FACE); 447 if (style == NULL) 448 return B_ERROR; 449 450 fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style, 451 DEFAULT_FONT_SIZE)); 452 if (!fDefaultPlainFont.IsSet()) 453 return B_NO_MEMORY; 454 455 // bold font 456 style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, 457 FALLBACK_BOLD_FONT_FAMILY, FALLBACK_BOLD_FONT_STYLE, B_BOLD_FACE); 458 459 fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style, 460 DEFAULT_FONT_SIZE)); 461 if (!fDefaultBoldFont.IsSet()) 462 return B_NO_MEMORY; 463 464 // fixed font 465 style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, 466 FALLBACK_FIXED_FONT_FAMILY, FALLBACK_FIXED_FONT_STYLE, B_REGULAR_FACE); 467 468 fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style, 469 DEFAULT_FONT_SIZE)); 470 if (!fDefaultFixedFont.IsSet()) 471 return B_NO_MEMORY; 472 473 fDefaultFixedFont->SetSpacing(B_FIXED_SPACING); 474 475 return B_OK; 476 } 477 478 479 /*! \brief Removes the style from the font directory. 480 481 It doesn't necessary delete the font style, if it's still 482 in use, though. 483 */ 484 void 485 GlobalFontManager::_RemoveStyle(font_directory& directory, FontStyle* style) 486 { 487 FTRACE(("font removed: %s\n", style->Name())); 488 489 directory.styles.RemoveItem(style); 490 directory.revision++; 491 492 _RemoveFont(style->Family()->ID(), style->ID()); 493 } 494 495 496 void 497 GlobalFontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node) 498 { 499 // remove font style from directory 500 node_ref nodeRef; 501 nodeRef.device = device; 502 nodeRef.node = directoryNode; 503 504 font_directory* directory = _FindDirectory(nodeRef); 505 if (directory != NULL) { 506 // find style in directory and remove it 507 nodeRef.node = node; 508 FontStyle* style; 509 while ((style = directory->FindStyle(nodeRef)) != NULL) 510 _RemoveStyle(*directory, style); 511 } 512 } 513 514 515 /*! \brief Counts the number of font families available 516 \return The number of unique font families currently available 517 */ 518 int32 519 GlobalFontManager::CountFamilies() 520 { 521 _ScanFontsIfNecessary(); 522 523 return FontManager::CountFamilies(); 524 } 525 526 527 /*! \brief Counts the number of styles available in a font family 528 \param family Name of the font family to scan 529 \return The number of font styles currently available for the font family 530 */ 531 int32 532 GlobalFontManager::CountStyles(const char* familyName) 533 { 534 _ScanFontsIfNecessary(); 535 536 FontFamily* family = GetFamily(familyName); 537 if (family) 538 return family->CountStyles(); 539 540 return 0; 541 } 542 543 544 /*! \brief Counts the number of styles available in a font family 545 \param family Name of the font family to scan 546 \return The number of font styles currently available for the font family 547 */ 548 int32 549 GlobalFontManager::CountStyles(uint16 familyID) 550 { 551 _ScanFontsIfNecessary(); 552 553 FontFamily* family = GetFamily(familyID); 554 if (family) 555 return family->CountStyles(); 556 557 return 0; 558 } 559 560 561 FontStyle* 562 GlobalFontManager::GetStyle(uint16 familyID, uint16 styleID) const 563 { 564 return FontManager::GetStyle(familyID, styleID); 565 } 566 567 568 /*! \brief Retrieves the FontStyle object that comes closest to the one 569 specified. 570 571 \param family The font's family or NULL in which case \a familyID is used 572 \param style The font's style or NULL in which case \a styleID is used 573 \param familyID will only be used if \a family is NULL (or empty) 574 \param styleID will only be used if \a style is NULL (or empty) 575 \param face is used to specify the style if both \a style is NULL or empty 576 and styleID is 0xffff. 577 578 \return The FontStyle having those attributes or NULL if not available 579 */ 580 FontStyle* 581 GlobalFontManager::GetStyle(const char* familyName, const char* styleName, 582 uint16 familyID, uint16 styleID, uint16 face) 583 { 584 ASSERT(IsLocked()); 585 586 FontFamily* family; 587 588 // find family 589 590 if (familyName != NULL && familyName[0]) 591 family = GetFamily(familyName); 592 else 593 family = GetFamily(familyID); 594 595 if (family == NULL) 596 return NULL; 597 598 // find style 599 600 if (styleName != NULL && styleName[0]) { 601 FontStyle* fontStyle = family->GetStyle(styleName); 602 if (fontStyle != NULL) 603 return fontStyle; 604 605 // before we fail, we try the mappings for a match 606 if (_AddMappedFont(family->Name(), styleName) == B_OK) { 607 fontStyle = family->GetStyle(styleName); 608 if (fontStyle != NULL) 609 return fontStyle; 610 } 611 612 _ScanFonts(); 613 return family->GetStyle(styleName); 614 } 615 616 if (styleID != 0xffff) 617 return family->GetStyleByID(styleID); 618 619 // try to get from face 620 return family->GetStyleMatchingFace(face); 621 } 622 623 624 void 625 GlobalFontManager::_PrecacheFontFile(const ServerFont* font) 626 { 627 if (font == NULL) 628 return; 629 630 size_t bufferSize = 32768; 631 uint8* buffer = new (std::nothrow) uint8[bufferSize]; 632 if (buffer == NULL) { 633 // We don't care. Pre-caching doesn't make sense anyways when there 634 // is not enough RAM... 635 return; 636 } 637 638 BFile file(font->Path(), B_READ_ONLY); 639 if (file.InitCheck() != B_OK) { 640 delete[] buffer; 641 return; 642 } 643 644 while (true) { 645 // We just want the file in the kernel file cache... 646 ssize_t read = file.Read(buffer, bufferSize); 647 if (read < (ssize_t)bufferSize) 648 break; 649 } 650 651 delete[] buffer; 652 } 653 654 655 void 656 GlobalFontManager::_AddSystemPaths() 657 { 658 BPath path; 659 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) 660 _AddPath(path.Path()); 661 662 // We don't scan these in test mode to help shave off some startup time 663 #if !TEST_MODE 664 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) 665 _AddPath(path.Path()); 666 #endif 667 } 668 669 670 void 671 GlobalFontManager::_ScanFontsIfNecessary() 672 { 673 if (!fScanned) 674 _ScanFonts(); 675 } 676 677 678 //! Scans all currently known font directories 679 void 680 GlobalFontManager::_ScanFonts() 681 { 682 if (fScanned) 683 return; 684 685 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 686 font_directory* directory = fDirectories.ItemAt(i); 687 688 if (directory->AlreadyScanned()) 689 continue; 690 691 _ScanFontDirectory(*directory); 692 } 693 694 fScanned = true; 695 } 696 697 698 /*! \brief Adds the FontFamily/FontStyle that is represented by this path. 699 */ 700 status_t 701 GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry) 702 { 703 node_ref nodeRef; 704 status_t status = entry.GetNodeRef(&nodeRef); 705 if (status < B_OK) 706 return status; 707 708 BPath path; 709 status = entry.GetPath(&path); 710 if (status < B_OK) 711 return status; 712 713 FT_Face face; 714 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -1, &face); 715 if (error != 0) 716 return B_ERROR; 717 FT_Long count = face->num_faces; 718 FT_Done_Face(face); 719 720 for (FT_Long i = 0; i < count; i++) { 721 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -(i + 1), &face); 722 if (error != 0) 723 return B_ERROR; 724 uint32 variableCount = (face->style_flags & 0x7fff0000) >> 16; 725 FT_Done_Face(face); 726 727 uint32 j = variableCount == 0 ? 0 : 1; 728 do { 729 FT_Long faceIndex = i | (j << 16); 730 error = FT_New_Face(gFreeTypeLibrary, path.Path(), faceIndex, &face); 731 if (error != 0) 732 return B_ERROR; 733 734 uint16 familyID, styleID; 735 status = FontManager::_AddFont(face, nodeRef, path.Path(), familyID, styleID); 736 if (status == B_NAME_IN_USE) { 737 status = B_OK; 738 j++; 739 continue; 740 } 741 if (status < B_OK) 742 return status; 743 directory.styles.AddItem(GetStyle(familyID, styleID)); 744 j++; 745 } while (j <= variableCount); 746 } 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 return FontManager::GetFamily(familyID); 985 } 986 987 988 const ServerFont* 989 GlobalFontManager::DefaultPlainFont() const 990 { 991 return fDefaultPlainFont.Get(); 992 } 993 994 995 const ServerFont* 996 GlobalFontManager::DefaultBoldFont() const 997 { 998 return fDefaultBoldFont.Get(); 999 } 1000 1001 1002 const ServerFont* 1003 GlobalFontManager::DefaultFixedFont() const 1004 { 1005 return fDefaultFixedFont.Get(); 1006 } 1007 1008 1009 void 1010 GlobalFontManager::AttachUser(uid_t userID) 1011 { 1012 BAutolock locker(this); 1013 1014 #if !TEST_MODE 1015 // TODO: actually, find_directory() cannot know which user ID we want here 1016 // TODO: avoids user fonts in safe mode 1017 BPath path; 1018 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) 1019 _AddPath(path.Path()); 1020 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) 1021 == B_OK) { 1022 _AddPath(path.Path()); 1023 } 1024 #endif 1025 } 1026 1027 1028 void 1029 GlobalFontManager::DetachUser(uid_t userID) 1030 { 1031 BAutolock locker(this); 1032 1033 // TODO! 1034 } 1035