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
47GlobalFontManager* gFontManager = NULL;
48extern FT_Library gFreeTypeLibrary;
49
50
51struct 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
63struct GlobalFontManager::font_mapping {
64	BString		family;
65	BString		style;
66	entry_ref	ref;
67};
68
69
70FontStyle*
71GlobalFontManager::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
84static status_t
85set_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
103GlobalFontManager::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
134GlobalFontManager::~GlobalFontManager()
135{
136	fDefaultPlainFont.Unset();
137	fDefaultBoldFont.Unset();
138	fDefaultFixedFont.Unset();
139
140	_RemoveAllFonts();
141
142	FT_Done_FreeType(gFreeTypeLibrary);
143}
144
145
146void
147GlobalFontManager::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
301int32
302GlobalFontManager::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
320void
321GlobalFontManager::SaveRecentFontMappings()
322{
323}
324
325
326void
327GlobalFontManager::_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
345bool
346GlobalFontManager::_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
374status_t
375GlobalFontManager::_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
415FontStyle*
416GlobalFontManager::_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*/
439status_t
440GlobalFontManager::_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*/
484void
485GlobalFontManager::_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
496void
497GlobalFontManager::_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*/
518int32
519GlobalFontManager::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*/
531int32
532GlobalFontManager::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*/
548int32
549GlobalFontManager::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
561FontStyle*
562GlobalFontManager::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*/
580FontStyle*
581GlobalFontManager::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
626void
627GlobalFontManager::_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
657void
658GlobalFontManager::_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
672void
673GlobalFontManager::_ScanFontsIfNecessary()
674{
675	if (!fScanned)
676		_ScanFonts();
677}
678
679
680//! Scans all currently known font directories
681void
682GlobalFontManager::_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*/
702status_t
703GlobalFontManager::_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
757GlobalFontManager::font_directory*
758GlobalFontManager::_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
771void
772GlobalFontManager::_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
786status_t
787GlobalFontManager::_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
798status_t
799GlobalFontManager::_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*/
863status_t
864GlobalFontManager::_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*/
910status_t
911GlobalFontManager::_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*/
961FontFamily*
962GlobalFontManager::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
983FontFamily*
984GlobalFontManager::GetFamily(uint16 familyID) const
985{
986	return FontManager::GetFamily(familyID);
987}
988
989
990const ServerFont*
991GlobalFontManager::DefaultPlainFont() const
992{
993	return fDefaultPlainFont.Get();
994}
995
996
997const ServerFont*
998GlobalFontManager::DefaultBoldFont() const
999{
1000	return fDefaultBoldFont.Get();
1001}
1002
1003
1004const ServerFont*
1005GlobalFontManager::DefaultFixedFont() const
1006{
1007	return fDefaultFixedFont.Get();
1008}
1009
1010
1011void
1012GlobalFontManager::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
1030void
1031GlobalFontManager::DetachUser(uid_t userID)
1032{
1033	BAutolock locker(this);
1034
1035	// TODO!
1036}
1037