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 family and \a style are 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 if (styleID != 0xffff && (familyName == NULL || !familyName[0]) 589 && (styleName == NULL || !styleName[0])) { 590 return GetStyle(familyID, styleID); 591 } 592 593 // find family 594 595 if (familyName != NULL && familyName[0]) 596 family = GetFamily(familyName); 597 else 598 family = GetFamily(familyID); 599 600 if (family == NULL) 601 return NULL; 602 603 // find style 604 605 if (styleName != NULL && styleName[0]) { 606 FontStyle* fontStyle = family->GetStyle(styleName); 607 if (fontStyle != NULL) 608 return fontStyle; 609 610 // before we fail, we try the mappings for a match 611 if (_AddMappedFont(family->Name(), styleName) == B_OK) { 612 fontStyle = family->GetStyle(styleName); 613 if (fontStyle != NULL) 614 return fontStyle; 615 } 616 617 _ScanFonts(); 618 return family->GetStyle(styleName); 619 } 620 621 // try to get from face 622 return family->GetStyleMatchingFace(face); 623 } 624 625 626 void 627 GlobalFontManager::_PrecacheFontFile(const ServerFont* font) 628 { 629 if (font == NULL) 630 return; 631 632 size_t bufferSize = 32768; 633 uint8* buffer = new (std::nothrow) uint8[bufferSize]; 634 if (buffer == NULL) { 635 // We don't care. Pre-caching doesn't make sense anyways when there 636 // is not enough RAM... 637 return; 638 } 639 640 BFile file(font->Path(), B_READ_ONLY); 641 if (file.InitCheck() != B_OK) { 642 delete[] buffer; 643 return; 644 } 645 646 while (true) { 647 // We just want the file in the kernel file cache... 648 ssize_t read = file.Read(buffer, bufferSize); 649 if (read < (ssize_t)bufferSize) 650 break; 651 } 652 653 delete[] buffer; 654 } 655 656 657 void 658 GlobalFontManager::_AddSystemPaths() 659 { 660 BPath path; 661 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) 662 _AddPath(path.Path()); 663 664 // We don't scan these in test mode to help shave off some startup time 665 #if !TEST_MODE 666 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) 667 _AddPath(path.Path()); 668 #endif 669 } 670 671 672 void 673 GlobalFontManager::_ScanFontsIfNecessary() 674 { 675 if (!fScanned) 676 _ScanFonts(); 677 } 678 679 680 //! Scans all currently known font directories 681 void 682 GlobalFontManager::_ScanFonts() 683 { 684 if (fScanned) 685 return; 686 687 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 688 font_directory* directory = fDirectories.ItemAt(i); 689 690 if (directory->AlreadyScanned()) 691 continue; 692 693 _ScanFontDirectory(*directory); 694 } 695 696 fScanned = true; 697 } 698 699 700 /*! \brief Adds the FontFamily/FontStyle that is represented by this path. 701 */ 702 status_t 703 GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry) 704 { 705 node_ref nodeRef; 706 status_t status = entry.GetNodeRef(&nodeRef); 707 if (status < B_OK) 708 return status; 709 710 BPath path; 711 status = entry.GetPath(&path); 712 if (status < B_OK) 713 return status; 714 715 FT_Face face; 716 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -1, &face); 717 if (error != 0) 718 return B_ERROR; 719 FT_Long count = face->num_faces; 720 FT_Done_Face(face); 721 722 for (FT_Long i = 0; i < count; i++) { 723 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -(i + 1), &face); 724 if (error != 0) 725 return B_ERROR; 726 uint32 variableCount = (face->style_flags & 0x7fff0000) >> 16; 727 FT_Done_Face(face); 728 729 uint32 j = variableCount == 0 ? 0 : 1; 730 do { 731 FT_Long faceIndex = i | (j << 16); 732 error = FT_New_Face(gFreeTypeLibrary, path.Path(), faceIndex, &face); 733 if (error != 0) 734 return B_ERROR; 735 736 uint16 familyID, styleID; 737 status = FontManager::_AddFont(face, nodeRef, path.Path(), familyID, styleID); 738 if (status == B_NAME_IN_USE) { 739 status = B_OK; 740 j++; 741 continue; 742 } 743 if (status < B_OK) 744 return status; 745 directory.styles.AddItem(GetStyle(familyID, styleID)); 746 j++; 747 } while (j <= variableCount); 748 } 749 750 if (directory.AlreadyScanned()) 751 directory.revision++; 752 753 return B_OK; 754 } 755 756 757 GlobalFontManager::font_directory* 758 GlobalFontManager::_FindDirectory(node_ref& nodeRef) 759 { 760 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 761 font_directory* directory = fDirectories.ItemAt(i); 762 763 if (directory->directory == nodeRef) 764 return directory; 765 } 766 767 return NULL; 768 } 769 770 771 void 772 GlobalFontManager::_RemoveDirectory(font_directory* directory) 773 { 774 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n", 775 directory->directory.node)); 776 777 fDirectories.RemoveItem(directory, false); 778 779 // TODO: remove styles from this directory! 780 781 watch_node(&directory->directory, B_STOP_WATCHING, this); 782 delete directory; 783 } 784 785 786 status_t 787 GlobalFontManager::_AddPath(const char* path) 788 { 789 BEntry entry; 790 status_t status = entry.SetTo(path); 791 if (status != B_OK) 792 return status; 793 794 return _AddPath(entry); 795 } 796 797 798 status_t 799 GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory) 800 { 801 node_ref nodeRef; 802 status_t status = entry.GetNodeRef(&nodeRef); 803 if (status != B_OK) 804 return status; 805 806 // check if we are already know this directory 807 808 font_directory* directory = _FindDirectory(nodeRef); 809 if (directory != NULL) { 810 if (_newDirectory) 811 *_newDirectory = directory; 812 return B_OK; 813 } 814 815 // it's a new one, so let's add it 816 817 directory = new (std::nothrow) font_directory; 818 if (directory == NULL) 819 return B_NO_MEMORY; 820 821 struct stat stat; 822 status = entry.GetStat(&stat); 823 if (status != B_OK) { 824 delete directory; 825 return status; 826 } 827 828 directory->directory = nodeRef; 829 directory->user = stat.st_uid; 830 directory->group = stat.st_gid; 831 directory->revision = 0; 832 833 status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this); 834 if (status != B_OK) { 835 // we cannot watch this directory - while this is unfortunate, 836 // it's not a critical error 837 printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n", 838 nodeRef.device, nodeRef.node); 839 // TODO: should go into syslog() 840 } else { 841 BPath path(&entry); 842 FTRACE(("FontManager: now watching: %s\n", path.Path())); 843 } 844 845 fDirectories.AddItem(directory); 846 847 if (_newDirectory) 848 *_newDirectory = directory; 849 850 fScanned = false; 851 return B_OK; 852 } 853 854 855 /*! \brief Creates all unknown font_directories of the specified path - but 856 only if one of its parent directories is already known. 857 858 This method is used to create the font_directories for font_mappings. 859 It recursively walks upwards in the directory hierarchy until it finds 860 a known font_directory (or hits the root directory, in which case it 861 bails out). 862 */ 863 status_t 864 GlobalFontManager::_CreateDirectories(const char* path) 865 { 866 FTRACE(("_CreateDirectories(path = %s)\n", path)); 867 868 if (!strcmp(path, "/")) { 869 // we walked our way up to the root 870 return B_ENTRY_NOT_FOUND; 871 } 872 873 BEntry entry; 874 status_t status = entry.SetTo(path); 875 if (status != B_OK) 876 return status; 877 878 node_ref nodeRef; 879 status = entry.GetNodeRef(&nodeRef); 880 if (status != B_OK) 881 return status; 882 883 // check if we are already know this directory 884 885 font_directory* directory = _FindDirectory(nodeRef); 886 if (directory != NULL) 887 return B_OK; 888 889 // We don't know this one yet - keep walking the path upwards 890 // and try to find a match. 891 892 BPath parent(path); 893 status = parent.GetParent(&parent); 894 if (status != B_OK) 895 return status; 896 897 status = _CreateDirectories(parent.Path()); 898 if (status != B_OK) 899 return status; 900 901 // We have our match, create sub directory 902 903 return _AddPath(path); 904 } 905 906 907 /*! \brief Scan a folder for all valid fonts 908 \param directoryPath Path of the folder to scan. 909 */ 910 status_t 911 GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory) 912 { 913 // This bad boy does all the real work. It loads each entry in the 914 // directory. If a valid font file, it adds both the family and the style. 915 916 BDirectory directory; 917 status_t status = directory.SetTo(&fontDirectory.directory); 918 if (status != B_OK) 919 return status; 920 921 BEntry entry; 922 while (directory.GetNextEntry(&entry) == B_OK) { 923 if (entry.IsDirectory()) { 924 // scan this directory recursively 925 font_directory* newDirectory; 926 if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL) 927 _ScanFontDirectory(*newDirectory); 928 929 continue; 930 } 931 932 // TODO: Commenting this out makes my "Unicode glyph lookup" 933 // work with our default fonts. The real fix is to select the 934 // Unicode char map (if supported), and/or adjust the 935 // utf8 -> glyph-index mapping everywhere to handle other 936 // char maps. We could also ignore fonts that don't support 937 // the Unicode lookup as a temporary "solution". 938 #if 0 939 FT_CharMap charmap = _GetSupportedCharmap(face); 940 if (!charmap) { 941 FT_Done_Face(face); 942 continue; 943 } 944 945 face->charmap = charmap; 946 #endif 947 948 _AddFont(fontDirectory, entry); 949 // takes over ownership of the FT_Face object 950 } 951 952 fontDirectory.revision = 1; 953 return B_OK; 954 } 955 956 957 /*! \brief Locates a FontFamily object by name 958 \param name The family to find 959 \return Pointer to the specified family or NULL if not found. 960 */ 961 FontFamily* 962 GlobalFontManager::GetFamily(const char* name) 963 { 964 if (name == NULL) 965 return NULL; 966 967 FontFamily* family = _FindFamily(name); 968 if (family != NULL) 969 return family; 970 971 if (fScanned) 972 return NULL; 973 974 // try font mappings before failing 975 if (_AddMappedFont(name) == B_OK) 976 return _FindFamily(name); 977 978 _ScanFonts(); 979 return _FindFamily(name); 980 } 981 982 983 FontFamily* 984 GlobalFontManager::GetFamily(uint16 familyID) const 985 { 986 return FontManager::GetFamily(familyID); 987 } 988 989 990 const ServerFont* 991 GlobalFontManager::DefaultPlainFont() const 992 { 993 return fDefaultPlainFont.Get(); 994 } 995 996 997 const ServerFont* 998 GlobalFontManager::DefaultBoldFont() const 999 { 1000 return fDefaultBoldFont.Get(); 1001 } 1002 1003 1004 const ServerFont* 1005 GlobalFontManager::DefaultFixedFont() const 1006 { 1007 return fDefaultFixedFont.Get(); 1008 } 1009 1010 1011 void 1012 GlobalFontManager::AttachUser(uid_t userID) 1013 { 1014 BAutolock locker(this); 1015 1016 #if !TEST_MODE 1017 // TODO: actually, find_directory() cannot know which user ID we want here 1018 // TODO: avoids user fonts in safe mode 1019 BPath path; 1020 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) 1021 _AddPath(path.Path()); 1022 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) 1023 == B_OK) { 1024 _AddPath(path.Path()); 1025 } 1026 #endif 1027 } 1028 1029 1030 void 1031 GlobalFontManager::DetachUser(uid_t userID) 1032 { 1033 BAutolock locker(this); 1034 1035 // TODO! 1036 } 1037