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