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