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