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