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